auth.user refactor: the profile aproach

799 visualizzazioni
Passa al primo messaggio da leggere

Jacob Kaplan-Moss

da leggere,
2 apr 2012, 20:35:4102/04/12
a django-d...@googlegroups.com
Hi folks --

I've written up a proposal for how *I* would like to address refactoring auth.user: https://gist.github.com/2245327.

In essence, this does two things:

* Vastly "prunes" the required fields on auth.user. The only things left are an "identifier" (which could be username, email, url, uuid, whatever), and a password.
* Introduces a new "profile" system that provides a way to contribute extra related fields. Multiple profiles are supported, along with some syntactic sugar for dealing with multiple profiles in a reasonably reusable way.

And that's about it. I'm deliberately trying to find a middle ground between "do the minimum to allow people to move on" and "throw out and rewrite django.contrib.auth entirely". I'm not expecting everyone to be thrilled by this idea, but I'm hoping that this is "Good Enough" for almost everyone.

For more please see the document. Please do try to read the whole thing: I've had a few rounds of feedback incorporated already, and there's even an FAQ at the end.

I'm not using BDFL fiat here, at least not yet. This is a proposal, and I very much want to hear feedback, objections, and alternatives. I'm particularly interested in hearing from people who've got complicated auth needs and think this absolutely won't work for them. 

I *have* reviewed all the other proposals and I'm between -0 and -1 on all of them. This means that if you don't like my proposal, you'll probably have to come up with a complete *new* idea to have any chance of getting my vote.

Thanks!

Jacob

Anssi Kääriäinen

da leggere,
2 apr 2012, 21:17:4402/04/12
a Django developers
FWIW I am +1 on this proposal. This is assuming that forms, queryset
handling of the data and so on actually work correctly and nicely. I
don't see a blocker in those areas, but before the code is actually
written and tested it is impossible to say if there will be problems.
Devil is in the details and all that.

I hope this will not result in hacks to allow .filter(data__) and so
on. I believe we should aim for as generic solutions as possible. If
we do hacks just for the User model, they _are_ going to cause
problems later on. Most of the features needed would be neat to have
regardless of the User refactor. Custom lookups per model/field for
example would be _really_ neat feature. The downside of aiming for
generic solutions is that we once again get into the situation where
User refactor is blocked by implementing foo, bar and baz before it.

I believe this is the right way forward. Multiple profiles makes sense
in the SQL world, and in the NoSQL world you would just embed the
profiles into the User document. I don't believe performance will be a
problem in practice, at least as long as you don't add too many
different profiles to be loaded by default. The most common use case
will just get better support: you have user profile containing three
fields, and then you have all your local fields defined in one
profile. As now, you have profile + user, now they just model your
data properly.

I don't see a proposal of how __unicode__ is going to be handled (the
Hello, UserName requirement). My proposal: go through the profiles in
the order they are defined, and pick the first one having __unicode__
defined as the default __unicode__. If there is no such profile,
return identifier.

- Anssi

Donald Stufft

da leggere,
2 apr 2012, 21:20:0202/04/12
a django-d...@googlegroups.com
If i recall on IRC the decider was to just create a display field (e.g. user.data["display"]) that the default profiles can provide (and can be overridden by other profiles of course).
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Anssi Kääriäinen

da leggere,
2 apr 2012, 21:25:4602/04/12
a Django developers
On Apr 3, 4:20 am, Donald Stufft <donald.stu...@gmail.com> wrote:
> If i recall on IRC the decider was to just create a display field (e.g. user.data["display"]) that the default profiles can provide (and can be overridden by other profiles of course).

My problem with this is that for example where I work the display
field would contain '%s, %s (%s)' % (self.lastname, self.firstname,
self.empl_no). I would not like to do that data duplication. If the
'display' can be a property then fine. But why not go directly for
__unicode__ in that case?

- Anssi

Donald Stufft

da leggere,
2 apr 2012, 21:27:3802/04/12
a django-d...@googlegroups.com
If we use __unicode__ (which i'm fine with) then it needs to follow the same resolution path as user.data[] does.

Alex Ogier

da leggere,
2 apr 2012, 22:56:5702/04/12
a django-d...@googlegroups.com
I realize that arguing with a BDFL might get me nowhere, but I don't think that multi-profile + select_related + proxy attributes on the user model is the proper approach for users going forward. The proposal makes some basic sense as an incremental improvement on the current status quo of a built-in user with fixed 30-character identifier and single one-to-one profile. But in the larger scheme of things it moves further in the wrong direction, in my opinion, towards a model that addresses people's specific 80% questions (email-as-identifier, packageable extra fields) but not the general best-of-all-worlds mechanisms.

Consider the other batteries-included frameworks out there. I think the three biggest ones are probably Ruby on Rails, ASP.NET MVC, and Play Framework. Something all three have in common, something I think would be valuable for Django, is that their User identity is absent, and authentication and authorization modules are added to a developer-created User model ad-hoc. I think it's fair to say, the ecosystem of third-party authentication mechanisms surrounding all three of these competitor frameworks is MUCH healthier than Django's, for the simple reason that it is much more flexible and sane to define your own user and plug in an authentication module then it is to plug authentication into a fixed user model that magically proxies back (even one as simple as a varchar identity + password).

The basic idea is that "authentication" is something that can be provided for any model you like. When a user authenticates, they are providing some sort of authentication token to your project that proves they are who they say they are, and as a developer you are free to attach this authentication token to *whatever you like,* not only contrib.auth.models.User. There is a rich ecosystem of third party authentication apps for our competitor frameworks that all work on this principle. They can provide authentication flows that bounce the user back and forth to twitter.com, their oauth provider, browserid, etc. and it is precisely because they don't demand anything from a central object. Even something as simple as "all user.identifiers that start with 'oauth2$' belong to our auth mechanism" starts to open all sorts of security holes. What if someone wants to authenticate by email+password OR by third party proof of email ownership (my university has a mechanism like this)? Well, if someone can manage to register an account belonging to 'aog...@princeton.edu', maybe by triggering some obscure email change recovery form wizard or something, then suddenly they possess my account on Django.

This is why I think the only sane long-term solution involves distinct and pluggable authentication modules, and a concept of users that doesn't enforce any brand of identity. The second stipulation is very important for social reasons, if only to ensure that the path of least resistance for third-party authentication doesn't involve trying to overload identity mechanisms for disparate and incompatible purposes.

JKM, you seem concerned that the notion of pluggable Users by necessity involves magical settings.

I'm convinced that such an idea is ultimately a bad idea: it allows apps exert action at a distance over other apps. It would allow the idea of a user to completely change without any warning simply by modifying a setting.

I am not convinced that this must be so. Asking a developer to write their own User model is not the same thing as automagically reshaping contrib.auth.models.User based on settings. A developer-defined notion of identity is a thing that should be codified in software by constructing a model. This community seems fixed on the idea that whatever model the developer comes up with to satisfy the bizarre constraints of his particular website, it must eventually be mounted at auth.User lest the world come crashing down as foreign keys break, middleware throws exceptions and California slides into the pacific ocean.

Nearly every other framework out there does this the opposite way: to authenticate with a third-party service, you add fields to your identity model to support whatever credentials are necessary. Not the other way round, where auth modules define models that have OneToOneFields to auth.User that uses an AUTH_PROFILES setting to magically proxy back.

I get that Django's core is very accustomed to the relational database mode of thinking: "If a User might own a Twitter handle, then let's create a table of twitter handles in the twitter-auth app, and foreign key back to the default User model". It's really not that bad to go the other way for a moment, and say "If a User might own a Twitter handle, then let's add a twitter_handle field to Users." The reason being that the latter is *so much more flexible*. You can simulate the first with the second, but not vice versa. Twitter-auth might not need its own table (in fact, it shouldn't need its own table). If you really wanted to, you could make one, and foreign-key *from* the user model which gives you everything the first solution has, with no need to created magical .prof1, .prof2 proxy attributes. You could even let users sign in with multiple handles with a many-to-many. Heck, maybe your blog posts have their own twitter credentials, I don't know.

So here's the short version of my proposal, which I plan on writing up in full tomorrow: Break auth as it stands into reusable pieces. Let people write their own user models, optionally using those pieces. Provide straightforward settings for any contrib apps that absolutely *must* have a specific model to key on (ideally none, but comments and admin probably need shims). Document the new wave of best practices, borrowed straight from the frameworks that got this right from the start: Write your own user, decide what identity means (and hence, what your login forms look like), add in whatever authentication mechanisms you like, add in whatever authorization mechanisms you need (with specific instructions on what contrib.admin demands from your model), and run with that.

Sorry for the rant, hopefully I'm not burning too many bridges,
Alex Ogier

Carl Meyer

da leggere,
2 apr 2012, 22:57:4602/04/12
a django-d...@googlegroups.com
On 04/02/2012 06:35 PM, Jacob Kaplan-Moss wrote:
> I've written up a proposal for how *I* would like to address refactoring
> auth.user: https://gist.github.com/2245327.

+1 from me.

One minorish nit: I think that "in the face of ambiguity, refuse the
temptation to guess" should apply equally to reading or writing
user.data, and that there shouldn't be an awkward discrepancy between
reading and writing. Thus, if you've got multiple profiles with
overlapping attributes (the less-common case), you need to be explicit
with AUTH_PROFILES in order to either read or write the overlapping keys
via user.data (but writing is then allowed). In the common case of no
overlapping keys, of course, you don't have to worry about AUTH_PROFILES.

Carl

signature.asc

Russell Keith-Magee

da leggere,
2 apr 2012, 22:59:5902/04/12
a django-d...@googlegroups.com

On 03/04/2012, at 8:35 AM, Jacob Kaplan-Moss wrote:

> Hi folks --
>
> I've written up a proposal for how *I* would like to address refactoring auth.user: https://gist.github.com/2245327.
>
> In essence, this does two things:
>
> * Vastly "prunes" the required fields on auth.user. The only things left are an "identifier" (which could be username, email, url, uuid, whatever), and a password.
> * Introduces a new "profile" system that provides a way to contribute extra related fields. Multiple profiles are supported, along with some syntactic sugar for dealing with multiple profiles in a reasonably reusable way.
>
> And that's about it. I'm deliberately trying to find a middle ground between "do the minimum to allow people to move on" and "throw out and rewrite django.contrib.auth entirely". I'm not expecting everyone to be thrilled by this idea, but I'm hoping that this is "Good Enough" for almost everyone.
>
> For more please see the document. Please do try to read the whole thing: I've had a few rounds of feedback incorporated already, and there's even an FAQ at the end.

I've added a summary of this option to the wiki:

https://code.djangoproject.com/wiki/ContribAuthImprovements#Solution5:Profile-basedsingleusermodel

As always, feel free to correct/update as necessary.

From my reading of the proposal, here are some questions/edge cases. For some of these questions, I fully expect the answer may be "You just can't do that"; however, given that they're plausible edge cases, it's worth being explicit about what we're aiming at.

* Auto-profile creation:

What if my profile is:

class MyProfile(Profile):
first_name = CharField(max_length=100)
last_name = CharField(max_length=100)

i.e., first_name and last_name are both required fields. How does the profile get automatically instantiated in this case? Doesn't the auto-instantiation of profiles essentially mean that there can be no required fields on a profile (or, at least, on an auto-instantiated profile)?

* Regarding AUTH_PROFILE and collisions:

What if I have 2+ profiles, but no clear order of precedence? e.g.,

class MyProfile1(Profile):
name = CharField()
email = EmailField()
color = CharField()

class MyProfile2(Profile):
name = CharField(unique=True)
email = EmailField()
age = IntegerField()

Lets say that for some internal logic reason for profile processing, I need both email and age on MyProfile2, but name from MyProfile1. Under these circumstances, there's no way I can specify a single AUTH_PROFILE ordering that will satisfy my requirements. Is this a case where I have to resort to explicit naming?

Or, alternatively, should we be treating AUTH_PROFILE as a mechanism for purely specifying the resolution to specific lookup problems? i.e., instead of just specifying an order, we specify which model we want ambiguous fields to come from:

AUTH_PROFILE_LOOKUP = {
'name': 'myapp1.MyProfile1',
'email': 'myapp1.MyProfile2',
'color': 'myapp1.MyProfile2',
}

That way, if a field is unambiguous, it's returned from whatever model provides it; if a field is ambiguous, we return the one specified; and if the ambiguity isn't resolved, we return a KeyError (or catch this case as a validation error on startup).

* Required fields and AUTH_PROFILE

Combining the previous two points; what if MyProfile2 has a required field, but it isn't a field selected by data[]? e.g, in the previous example, MyProfile2.name is a required unique field; but if it isn't instantiated with useful data, the profile instances can't exist.

== Commentary ==

For me, the previous three edge cases essentially point to the fact that there can ultimately only be 1 profile for "core" user information. There will almost certainly be uses for multiple profiles (e.g., to store OpenID credentials) -- but then, this is already the case (I've got more than one project in the wild with multiple "UserProfile" objects, without using AUTH_USER_PROFILE). However, as soon as there are overlaps between profiles, you're going to end up with partially populated profiles, and eventually someone will write a 'unified' profile model for each project that doesn't have the overlap ...

... at which point, we've essentially arrived at a swappable User model, just with a required join to get profile data, and a bunch of magic attributes and ORM properties to hide the fact that the join exists.

I understand Django's history has examples where swappable models caused problems that we don't want to revisit. However, as I understand it, these problems were largely caused by the fact that it was *easy* to accidentally swap out a model, and that change wouldn't be effectively communicated to other developers on the same codebase until the magic smoke escaped.

It's also worth pointing out that the problems with swappable models aren't really avoided by the profile-based approach. If I've got a project with N user profile models, grabbing user.data['name'] from one of them, another developer can very easily modify the order of AUTH_PROFILES, or modify the apps in INSTALLED_APPS to add to or change the user profiles that are currently in effect, or alter the precedence in AUTH_PROFILES to override the attribute we need. If the objection to swappable user models is that it's easy to swap out models, then it's just as easy to change the expectations for User models via a profile -- possibly easier, because you aren't modifying a single "AUTH_USER_MODEL" setting; you're changing one of several settings whose consequences *may* include changing the operation of the User model. AUTH_USER_MODEL at least has the consequences written on the box.

(User models are hard. Lets go shopping. :-)

One possible way to avoid this problem would be to make it harder to make these sorts of changes -- i.e., add project/install-level tracking for certain key settings. So, when you do a syncdb, we set a key-value pair in the database for the current value of AUTH_USER_MODEL (or whatever mechanism needs to be persisted). Django checks this value against the current settings.AUTH_USER_MODEL value on startup; if the setting value doesn't match the installation value, we can throw an error on startup.

Would this approach temper your objection to swappable models?

At the end of the day, I'm happy to go with a BDFL judgement on this. However, it's interesting to note that almost every time this problem arises, a "swappable" model of some sort is usually the initially proposed solution. That's because it's an obvious solution -- an obvious solution with problems, but an obvious solution, nonetheless. The alternative being proposed here isn't *clearly* better -- it's really just a matter of making a strategic choice to pick a different set of pain points.

To make matters works, it seems to me that it will require a lot more work to execute -- far from being "minimalist", it's invasive on auth code, ORM code, and (in the long term) the internals of every app that uses User.

For example, any code written against the fields on the existing auth.User will need to be updated to use the new data[] access mechanism. Yes, this will be introduced gradually over releases, but it doesn't change the fact that every app that currently accesses user.is_staff will need to update to user.data['is_staff'] at some point in the next few releases. By comparison, the swappable user approach generally means no changes to app code (or just modifying ForeignKey(User) to LazyForeignKey(User), or ForeignKey(settings.USER_MODEL)), and documenting that is_staff is a required attribute on User for using the app (a property that can be validated as a project integration requirement by a test suite).

There are also some aspects about this proposal that make my teeth itch -- filter(data__...), for example, strikes me as code that will be hard to write, hard to debug, and a PITA to maintain. And if you can filter(data__...), then you need to be able to order_by('data__...') as well. But all this complexity is required if you want to maintain decoupling of profiles. It also requires adding a feature to the ORM to support a specific use case in User, rather than a generic problem (although I suppose we could implement it in such a way as to make "profiles" an option for any other model that needs them).

If we're going to make a choice to avoid the "obvious" solution, I think we need to be very clear (both now, and for posterity) why we made the choices we have made, rather than trying to address or ameliorate the problems with the "obvious" solution.

Yours
Russ Magee %-)

Donald Stufft

da leggere,
2 apr 2012, 23:04:4102/04/12
a django-d...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Identity doesn't have anything to do with automatically dispatching users. All it is is a unique identifier. That's all this proposal honestly enforces that your users have. Some single piece of identifiable data that can be used to differentiate. This could be a username, or an email address. It could be a random string. Anything you want.

In your example you might have a TwitterProfile that provides 2 fields, "authenticated_by_twitter" and "twitter username". Then if you want to check how a person authenticated to your site, you'd merely check if user.data["authenticated_by_twitter"] was True. The identifier doesn't need to have that data codified in it, (but it could!) and I honestly do not think the statement "all users must have 1 single string of any length that uniquely identifies them" is that big of a burden.

Alex Ogier

da leggere,
2 apr 2012, 23:14:1202/04/12
a django-d...@googlegroups.com
On Mon, Apr 2, 2012 at 11:04 PM, Donald Stufft <donald...@gmail.com> wrote:
Identity doesn't have anything to do with automatically dispatching users. All it is is a unique identifier. That's all this proposal honestly enforces that your users have. Some single piece of identifiable data that can be used to differentiate. This could be a username, or an email address. It could be a random string. Anything you want.

In your example you might have a TwitterProfile that provides 2 fields, "authenticated_by_twitter" and "twitter username". Then if you want to check how a person authenticated to your site, you'd merely check if user.data["authenticated_by_twitter"] was True. The identifier doesn't need to have that data codified in it, (but it could!) and I honestly do not think the statement "all users must have 1 single string of any length that uniquely identifies them" is that big of a burden.

Perhaps I am more pessimistic than you, but I think it will quickly get out of hand. I mean, what reasonable developer would look at the user model as Jacob proposes, and wouldn't at least consider, "Well, I could make my own profile and ask everyone to add my app to AUTH_PROFILES and be magically joined on all User queries, or I could use the arbitrary length guaranteed-unique varchar to encode a whole mess of twitter credentials. I think I will choose option #2?"

Then as soon as everyone does that and facebook, twitter, browserid, and plain emails all share the same namespace, we open up the same whole can of worms that we get with cache keys, except now failures to manage things properly manifest themselves as security holes in basic authentication instead of cache collisions.

Best,
Alex Ogier

Tai Lee

da leggere,
3 apr 2012, 03:37:4703/04/12
a django-d...@googlegroups.com
I like this proposal because I am a big fan of a stripped down `User` model which is basically just an ID, whic provides a common hook into groups/permissions and other django and 3rd party profiles.

What I don't like is the magical `data` bag (accessing `User.data` and filter lookups), the new `AUTH_PROFILES` setting, and the idea of Django automagically or lazily creating profiles for me. I would rather see Django rely solely on its `OneToOneField` field to access user profile data.

Any pluggable app (app1) will know what fields it's own profile model has and how to access them, relative to the central `User` object.

If app1 relies on fields defined by another app (app2), that other app should be a requirement of app1 and app1 would know how to access app2's fields.

I am happy to use project-level signals for everything else (syncing common profile data from app1 and app2, or auto-creating user profiles), because project-level signals give me explicit control over what is going to happen and when. I don't need any more magic here.

Cheers.
Tai.

Donald Stufft

da leggere,
3 apr 2012, 03:41:2703/04/12
a django-d...@googlegroups.com
On Tuesday, April 3, 2012 at 3:37 AM, Tai Lee wrote:
I like this proposal because I am a big fan of a stripped down `User` model which is basically just an ID, whic provides a common hook into groups/permissions and other django and 3rd party profiles.

What I don't like is the magical `data` bag (accessing `User.data` and filter lookups), the new `AUTH_PROFILES` setting, and the idea of Django automagically or lazily creating profiles for me. I would rather see Django rely solely on its `OneToOneField` field to access user profile data.

Any pluggable app (app1) will know what fields it's own profile model has and how to access them, relative to the central `User` object.

If app1 relies on fields defined by another app (app2), that other app should be a requirement of app1 and app1 would know how to access app2's fields.

I am happy to use project-level signals for everything else (syncing common profile data from app1 and app2, or auto-creating user profiles), because project-level signals give me explicit control over what is going to happen and when. I don't need any more magic here.

Cheers.
Tai.
Without the `data` bag we've done nothing except rename the username field and make it longer really. Everything else that you define can already be achieved by ignoring the fields already on User and using OneToOneFields. With the `data` bag we allow a pattern kinda similar to class inheritance.

I think you also mistake the use case, it's not app1 relies on app2, it's app1 relies on _a_ field called email that provides an email address. It doesn't care where it comes from. If we do not provide a common way to access this data with this proposal, than a large number of apps are going to depend on the default profile, and we are stuck again here where everything depends on what Django Core decides for "first_name", "last_name", email length, etc.


On Tuesday, 3 April 2012 10:35:41 UTC+10, Jacob Kaplan-Moss wrote:
Hi folks --

I've written up a proposal for how *I* would like to address refactoring auth.user: https://gist.github.com/2245327.

In essence, this does two things:

* Vastly "prunes" the required fields on auth.user. The only things left are an "identifier" (which could be username, email, url, uuid, whatever), and a password.
* Introduces a new "profile" system that provides a way to contribute extra related fields. Multiple profiles are supported, along with some syntactic sugar for dealing with multiple profiles in a reasonably reusable way.

And that's about it. I'm deliberately trying to find a middle ground between "do the minimum to allow people to move on" and "throw out and rewrite django.contrib.auth entirely". I'm not expecting everyone to be thrilled by this idea, but I'm hoping that this is "Good Enough" for almost everyone.

For more please see the document. Please do try to read the whole thing: I've had a few rounds of feedback incorporated already, and there's even an FAQ at the end.

I'm not using BDFL fiat here, at least not yet. This is a proposal, and I very much want to hear feedback, objections, and alternatives. I'm particularly interested in hearing from people who've got complicated auth needs and think this absolutely won't work for them. 

I *have* reviewed all the other proposals and I'm between -0 and -1 on all of them. This means that if you don't like my proposal, you'll probably have to come up with a complete *new* idea to have any chance of getting my vote.

Thanks!

Jacob

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/PMpcPCKgTuoJ.

Bruno Renié

da leggere,
3 apr 2012, 04:50:3003/04/12
a django-d...@googlegroups.com
Hi Jacob,

Thanks for taking the time to tackle this issue!

On Tue, Apr 3, 2012 at 2:35 AM, Jacob Kaplan-Moss <ja...@jacobian.org> wrote:
> Hi folks --
>
> I've written up a proposal for how *I* would like to address refactoring
> auth.user: https://gist.github.com/2245327.
>
> In essence, this does two things:
>
> * Vastly "prunes" the required fields on auth.user. The only things left are
> an "identifier" (which could be username, email, url, uuid, whatever), and a
> password.
> * Introduces a new "profile" system that provides a way to contribute extra
> related fields. Multiple profiles are supported, along with some syntactic
> sugar for dealing with multiple profiles in a reasonably reusable way.

I very much like the idea of stripping down the user fields. I am
currently using an approach that's very similar to your proposal in
all my projects, except that instead of creating several profiles, I
directly add fields to the User model using this snippet:

https://gist.github.com/1283969 (I am not the initial author)

With this, adding a field to the user model is as simple as:

class MyUser(models.Model):
newsletter = models.BooleanField()

class Meta:
abstract = True
contribute_to_model(MyUser, User)

I already use this to patch the email field in the User model or make
group names not unique for instance.

The only issue is that the contributed models need to be registered as
early as possible. Currently I import them in my urls.py, but we can
imagine adding hooks for such things that need to be done at startup
time (app-refactor? :) or use your AUTH_PROFILES setting for this (not
enthusiastic about this one).

I am fully aware that this can be abused a lot. However this makes a
couple of things simpler:

* No need to proxy fields on the User model, it stays a normal model
with no special treatment
* Conflicts are simply resolved: if two models contribute the same
field, the last one registered wins
* No need for a special-cased form for editing profile-related fields,
a simple ModelForm with includes = ['fields', 'to', 'include'] works.

This is the technique I will use anyway if your proposal is
implemented, and I don't see anything that would prevent this from
working with the new auth so I'm mainly posting this because I haven't
seen this approach in the different proposals for the auth refactor.

-Bruno

Alex Ogier

da leggere,
3 apr 2012, 10:28:4003/04/12
a django-d...@googlegroups.com
Hi developers,

I have written up a little bit about the alternate proposal that I made a while ago, Solution 2a from https://code.djangoproject.com/wiki/ContribAuthImprovements

In addition to other arguments, there is a point-by-point breakdown of what I feel are the weaknesses in Jacob's proposal.

You can find it at  https://gist.github.com/2289395 

Best,
Alex Ogier


Jacob

--
You received this message because you are subscribed to the Google Groups "Django developers" group.

Andre Terra

da leggere,
3 apr 2012, 13:37:5303/04/12
a django-d...@googlegroups.com
DISCLAIMER: I'm just a hobbyist posting to an experienced developers' list.

I for one really like Jacob's approach. 

I may be missing something, but my only concern right now is being able to infer which profiles are attached to the User model at any given point. Moreover, I'd like to be able to catch that KeyError exception (which I would prefer to be wrapped in something more specific like "MultipleDataError") and handle it appropriately.

For example, drawing from the example in the gist, say I want to allow the user to change his 'name' for every associated profile he has. user.data.dict('name').keys() will show me the existing profiles, and I guess I could use these keys to access every profile... but it doesn't seem like the ideal solution. 

I can see the need for something like user.data.update({'name': 'Joe K. Smith'}) to forcefully update the name in every profile. In my humble newbie opinion, there's a lot of syntactic sugar to be added here to make working with multiple profiles as sweet as we want it to be. Things like user.profiles, or even defining a 'default' profile that point how MultipleDataErrors should be resolved.



Cheers,
AT

Luke Sneeringer

da leggere,
3 apr 2012, 14:46:1803/04/12
a django-d...@googlegroups.com
So, after reading this, I think I really only have a couple questions/concerns:

1. What, if anything, is the primary key on the base User model? Is it the identifier? If so, can it be specified as the primary key, rather than unique=True, db_index=True? If it's not the primary key, is it the usual magical id = models.AutoField()?

2. I'm a little nervous about the magical installation of Profile modules -- specifically, what happens once this system has been around for one to two years and we have a ton of these? I'm imagining a situation where there are lots of apps floating around, each with their own Profile subclasses that install behind the scenes when you install the app (and which you may or may not actually realize up front -- yes, a good developer *should* be aware of that, but that doesn't mean they will be...). What happens if a beginner writes everything against user.data['name'], and then installs an app that adds a new name, which is unordered and magically ends up on top, and therefore introduces new, unexpected, and, most importantly, extremely difficult to debug logic errors?

I agree with some of the criticisms of swappable user models, but at least the developer always knows what he is doing in that case. In this case, the behavior of your user model can change in unexpected ways when you do something that seems totally unrelated; some code that previously worked might either (1) return different data or (2) suddenly raise KeyError.

Perhaps this is the best of bad options. I agree there's a decent bit to be argued in favor of it, but can we figure out a less error-prone way for user.data to work? Honestly, I think it would be less risky to force everyone to write user.admin_profile.name -- it's more typing, but at least it's explicit. The current proposal for user.data doesn't actually pass "refuse the temptation to guess", because it glosses over the fact that developers *will guess* and then their code will stop working after what feels like a completely unrelated change.

3. Might I suggest that the Profile model that the admin requires be called AdminProfile and not DefaultProfile? It's really an admin need more than anything else.


Best Regard,
Luke



Carl Meyer

da leggere,
3 apr 2012, 16:31:5903/04/12
a django-d...@googlegroups.com
On 04/03/2012 08:28 AM, Alex Ogier wrote:
> I have written up a little bit about the alternate proposal that I made
> a while ago, Solution 2a
> from https://code.djangoproject.com/wiki/ContribAuthImprovements
>
> In addition to other arguments, there is a point-by-point breakdown of
> what I feel are the weaknesses in Jacob's proposal.
>
> You can find it at https://gist.github.com/2289395

Since I weighed in with a +1 on the linked-profiles approach, I should
say that I also find this proposal pretty compelling, and would be happy
to see us go either way. After reflecting on it a bit I think on balance
I prefer this proposal, for the following reasons:

1. Requires zero migration from people with existing projects who are
happy with the existing contrib.auth.User model. I think this is a big
plus: all the other proposals require every single installed Django
codebase to perform a migration.

2. Introduces fewer new-and-different one-off concepts into Django (the
data__ ORM special case, the automatic select_related special case, the
automatic collection of profile fields into user.data...). This also
translates into significantly less implementation work and less new
code, which is another big plus - we want something that'll actually get
done and not introduce a bunch of new code we need to maintain.

By the way, I took the liberty of removing from the wiki page the
references to models/settings circular dependencies, because AFAIK the
statements made about it on the wiki page were simply incorrect.
Importing settings does _not_ immediately loop through INSTALLED_APPS
and load every models file (that only happens if you run a management
command that performs model validation, and even then it happens only
after settings are fully loaded). And "from django.db import models"
itself imports settings, so if there were such a circular dependency
problem, every models.py in existence would already be suffering from
it. I frequently refer to settings in models.py and it does not cause a
problem. If anyone can provide sample code demonstrating that this is
actually a problem, feel free to correct me!

Carl

signature.asc

Adrian Holovaty

da leggere,
3 apr 2012, 16:34:1803/04/12
a django-d...@googlegroups.com
On Tue, Apr 3, 2012 at 9:28 AM, Alex Ogier <alex....@gmail.com> wrote:
> I have written up a little bit about the alternate proposal that I made a
> while ago, Solution 2a
> from https://code.djangoproject.com/wiki/ContribAuthImprovements

I just now got around to reading Jacob's solution and Alex's solution.
Thanks to everybody for the thoughts and impassioned debate so far.
Here's my take on it.

First, some background: I haven't used the built-in User module in
several years. I always write my own User model from scratch -- it's
so nice and clean. Want a twitter_username field? Just add it. No need
to add a convoluted foreign key or (oh god) one-to-one relationship to
some other table.

To me, this is the Right Way to do things. The framework should bend
to my needs, I shouldn't bend to the framework's needs.

Also, profile modules need to die. They needed to die circa 2006.

So, with that in mind, I've got to say I prefer Alex's solution. I
really think the right way to do it is:

1. Let you create your own User model, with whichever fields you want.

2. Provide a way to tell Django which model you're using for that.

3. In your own code, just deal with that model like you deal with any
other one. No need to jump through hoops.

4. Third-party models should be changed to use something like "user =
UserField()", which would automatically create a foreign key to the
registered User model. If you change your registered User model after
you've created those third-party tables, you're in for trouble. Don't
do that.

5. Django provides some generic APIs for getting at the registered
user. Example: a middleware that sets request.user based on the
current session.

6. Given that some third-party apps will likely want to get access to
common attributes of a User -- notably, email address -- there could
be some sort of standard interface that User models need to adhere to
(duck typing). So, a custom User model would say "for this User model,
the email address is stored in the database field called 'email'" --
or "this User model doesn't have email addresses."

I chatted about this with Jacob on IRC, and we reached consensus on
this approach. I'd like to get moving on this and would be happy to
take it on myself, starting next week.

Adrian

Daniel Sokolowski

da leggere,
3 apr 2012, 16:34:5603/04/12
a django-d...@googlegroups.com
Alex I have looked over your proposal and I agree on both your concerns with LFK, and Jacob’s approach; however I still find the profile approach is the most flexible solution.
 
Correct me if I’m wrong but both LFK or a swappable user model approach like your fail to address issue of extensibility. If today my project is authorizing with username and password and tomorrow I wish to add OpenAuth then my User model is replaced, whereas with Jacobs approach I add another profile model, yes? What about auth apps, you could only use one, with profiles many could co exist; one for Facebook, Twitter, etc.
 
Your point 4 claiming it’s undiscoverable is not entirely true, AUTH_PROFILE setting can be examined just as apps examine INSTALLED_APPS settings at run time. Your point 2 that being able to change the functionality so quickly is actually superior in my mind, it’s a lot more work to do a schema migration then the creation a of  new profile table.  Points 5 and 3 are good points and remind that there is no perfect solution to new auth.
 
To sum up I am curious to know how your approach handles additional user data and authorization schemes.
Alex Ogier

To unsubscribe from this group, send email to mailto:django-developers%2Bunsu...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Donald Stufft

da leggere,
3 apr 2012, 16:42:5403/04/12
a django-d...@googlegroups.com
Like Carl I was +1 on Profiles and I'm now leaning towards the Swappable User Models.

It's explicit (it only changes when you change the USER_MODEL setting).

It's Duck Typing which is Idiomatic in Python. ("This app depends on a user model that defines ``email`").

If you wish to add OpenID you'd make your UserModel quack like an OpenID Duck, as well as Quack like a Username/Password Duck.

Carl Meyer

da leggere,
3 apr 2012, 16:42:1003/04/12
a django-d...@googlegroups.com
On 04/03/2012 02:34 PM, Daniel Sokolowski wrote:
> Correct me if I’m wrong but both LFK or a swappable user model approach
> like your fail to address issue of extensibility. If today my project is
> authorizing with username and password and tomorrow I wish to add
> OpenAuth then my User model is replaced, whereas with Jacobs approach I
> add another profile model, yes? What about auth apps, you could only use
> one, with profiles many could co exist; one for Facebook, Twitter, etc.

You would add whatever authorization fields you need to your single user
model. If you add new kinds of authorization that require new fields on
the user model, you'd do a schema migration (just like with any other
modification to any other model).

A third-party app that wants to provide, say, Facebook integration,
could either do its own model with a one-to-one to user (nothing is
going to suddenly prevent that approach, it just won't have any extra
sugar around it), or could provide an abstract base class that your user
model can inherit from that includes the needed fields, or could just
document "to use this app, your user model must have these fields: ..."
and let you do it yourself.

Carl

signature.asc

Luke Sneeringer

da leggere,
3 apr 2012, 16:44:2803/04/12
a django-d...@googlegroups.com
+1. Also, I'd like to help. :)

L

Alex Ogier

da leggere,
3 apr 2012, 17:51:1603/04/12
a django-d...@googlegroups.com
Thanks for the response, Adrian.
 

4. Third-party models should be changed to use something like "user =
UserField()", which would automatically create a foreign key to the
registered User model. If you change your registered User model after
you've created those third-party tables, you're in for trouble. Don't
do that.

5. Django provides some generic APIs for getting at the registered
user. Example: a middleware that sets request.user based on the
current session.

 
I think convenience methods are nice and all, but we should be careful of making "The Registered User(tm)" too much of a baked-in concept. Ian Lewis made a great point a few weeks ago: There are domains in which it is frankly inappropriate for various authenticated users to mix in the same database table with the same uniqueness constraints. Maybe you run a medical record management site, and some of your users are administrative staff and doctors, and some are patients. Sure, you could manage this by having a unified model containing everything, and carefully validating with an "is_doctor" field the way contrib.admin handles "is_admin," but it might be more appropriate to have separate models. It becomes much easier to express authorization constraints such as, "As a hospital administrator, Ms. Sally Johnson is authorized to view the patient list of Dr. John Smith, but *absolutely not* his record as a patient himself," or "Patients like Dr. John Smith can be authenticated via Facebook Connect, but doctors need to be authenticated via our internal LDAP." You can have login ModelForms for doctors that log in to doctor records and the Django admin site, and login ModelForms for patients that let them view their own records through the frontend.
 
Obviously supporting multiple user models isn't something that should be a priority for Django, but locking people out of the choice seems unwise, when we can get the option "for free": just let authentication backends return any authenticated object at all to add as request.user. Python's duck typing will get you the rest of the way, except for those persnickety tables that absolutely must have a specific concrete table to foreignkey on. We already do something similar for AnonymousUsers, exposing a common interface at request.user that isn't backed up by the canonical User table. Generalizing that is A Good Idea in my opinion.
 
Anyways I look forward to seeing how this progresses. I'd love to be a part of making this happen. Would you like me to turn my work on breaking up auth.User into reusable chunks into a ticket on trac? I think it makes sense to merge no matter what we decide on for specific settings and access mechanisms to developer-defined users.
 
Best,
Alex Ogier

Russell Keith-Magee

da leggere,
3 apr 2012, 23:50:5903/04/12
a django-d...@googlegroups.com

On 04/04/2012, at 4:31 AM, Carl Meyer wrote:
>
> By the way, I took the liberty of removing from the wiki page the
> references to models/settings circular dependencies, because AFAIK the
> statements made about it on the wiki page were simply incorrect.
> Importing settings does _not_ immediately loop through INSTALLED_APPS
> and load every models file (that only happens if you run a management
> command that performs model validation, and even then it happens only
> after settings are fully loaded). And "from django.db import models"
> itself imports settings, so if there were such a circular dependency
> problem, every models.py in existence would already be suffering from
> it. I frequently refer to settings in models.py and it does not cause a
> problem. If anyone can provide sample code demonstrating that this is
> actually a problem, feel free to correct me!

Those comments were my doing. I swear that at some point in Django's past, importing settings in models was a source of problems. However, the fact that django.db.models clearly imports settings indicates that regardless of whether this problem existed in the past, it certainly isn't the case any more.

Yours,
Russ Magee %-)

Russell Keith-Magee

da leggere,
3 apr 2012, 23:56:3203/04/12
a django-d...@googlegroups.com

Extending on this -- User model requirements should be encoded as part of an integration test suite.

As an example of best practice, django.contrib.admin should implement a UserPropertiesTest that checks that the current USER_MODEL has an email field, and a get_permissions method, and so on. That way, if you swap in a user model that doesn't have the properties that the admin requires, it will get flagged as a issue during testing.

This should, of course, be backed up with a clear documentation of exactly what type of User-duck Django's admin requires.

Yours,
Russ Magee %-)

Russell Keith-Magee

da leggere,
4 apr 2012, 01:11:4404/04/12
a django-d...@googlegroups.com

On 04/04/2012, at 4:34 AM, Adrian Holovaty wrote:

> On Tue, Apr 3, 2012 at 9:28 AM, Alex Ogier <alex....@gmail.com> wrote:
>> I have written up a little bit about the alternate proposal that I made a
>> while ago, Solution 2a
>> from https://code.djangoproject.com/wiki/ContribAuthImprovements
>

> ...


> 4. Third-party models should be changed to use something like "user =
> UserField()", which would automatically create a foreign key to the
> registered User model. If you change your registered User model after
> you've created those third-party tables, you're in for trouble. Don't
> do that.
>

My only concern about this approach is making a special case of swappable Users. There's at least one other example of a swappable model in Django's core -- the Comments model -- and I have no doubt that we could find other examples with a quick survey.

If we're going down the path of swappable models, I'd rather set up the infrastructure to solve the *general* problem of swappable models, rather than just swappable *user* models specifically.

If this required a massive increase in the effort required, I can see how practicality would determine that we just solve the immediate problem. However, in this case, it's really just a matter of avoiding User-specific naming on 4 features:

a) Specifying the model that can be swapped.

This is just the name of the setting. USER_MODEL is fine by itself, and has obvious analogs with other models. However, an alternate way of spelling the same thing would be to have a single setting for all swappable models:

SWAPPABLE_MODELS = {
'user': 'myauth.SuperDuperUser',
'comment': 'comments.Comment',
}

in which you can define any extensible model, and provide a convenient key (e.g., 'user') to identify the value for that model.

b) Preventing the "default" model from being added to the app cache, or synchronized to the database

contrib.auth is going to need ship with a User model, but we really don't want an auth_user table to be created, or get_model(auth,'User') to resolve, if the default User isn't being used. Same goes for any other swappable model.

Rather than make a special case of User inside syncdb, or nesting model definitions in if blocks, lets add a "swappable" attribute to Meta. If swappable is defined, syncdb checks the contents of SWAPPABLE_MODELS to see if this model has been substituted; if it has, then the table isn't created, and the model isn't added to the app cache. The value of pluggable can match the key in SWAPPABLE_MODELS (so auth.User would define swappable='user', following the previous example)

This can also act as a safety mechanism; if a developer has an app that contains a ForeignKey(User), and the User model has been swapped out, this can now raise a validation warning (notifying the developer that the User model could potentially be swapped out) or error (in the case where the model *has* been swapped out).

c) Providing a way to specify foreign keys to that model

Rather than introduce a User-specific UserField(), introduce a generic LazyForeignKey('user') - both of which are effectively just ForeignKey(settings.XXXX) anyway.

d) Provide a way to discover the current user model

Rather than have auth.get_user_model(), leverage the existing app cache. We already have get_model(); it wouldn't be too hard to add a get_swappable_model('user').

> 6. Given that some third-party apps will likely want to get access to
> common attributes of a User -- notably, email address -- there could
> be some sort of standard interface that User models need to adhere to
> (duck typing). So, a custom User model would say "for this User model,
> the email address is stored in the database field called 'email'" --
> or "this User model doesn't have email addresses."
>

As I've noted elsewhere, this can be both a documentation issue, and backed up by an integration test. As a longer term goal, if someone ever takes the bait and does the Validation refactor for the GSoC (hint hint), it could also be extracted as a validation condition for an app.

Yours,
Russ Magee %-)


Eric Florenzano

da leggere,
4 apr 2012, 01:25:4004/04/12
a django-d...@googlegroups.com
I completely agree with Adrian's proposal, with these amendments suggested by Russell in order to build something slightly more generic, like LazyForeignKey, instead of UserField.  In fact, a UserField coud end up being provided as a subclass of LazyForeignKey with a default name provided (e.g. 'user') and any other user-domain-specific customizations needed.

Thanks,
Eric Florenzano

bhuztez

da leggere,
4 apr 2012, 03:31:5804/04/12
a Django developers
django.contrib.auth.get_user already uses BACKEND_SESSION_KEY to
decide from which backend to get user object.

To support multiple user models, we can just change the
AUTHENTICATION_BACKENDS setting to a dict just as DATABASES.

AUTHENTICATION_BACKENDS = {
'default': {
'BACKEND': 'django.contrib.auth.backends.ModelBackend',
'MODEL': 'django.contrib.auth.models.User',
},
}

Instead of a single global USER_MODEL setting, each app should have
its own USER_MODEL settings, like ADMIN_USER_MODEL,
COMMENTS_USER_MODEL .

I do not like `ForeignKey(settings.ADMIN_USER_MODEL or
settings.USER_MODEL)` . I prefer a combination of solution 2a and 2b
to that.

Tai Lee

da leggere,
4 apr 2012, 08:44:0704/04/12
a django-d...@googlegroups.com
I'm not so sure that it's necessary or even desirable to solve the "general" problem of swappable models. If anyone can swap any model by changing a setting, that sounds like a recipe for confusion to me.

Seems to me that the main benefit of the swappable models approach is just to avoid backwards incompatibility issues by making the change opt-in, but I think it's also opening Pandora's box. I think that leaving it up to developers to ensure that their swapped-in models simply behave enough like the original is going to cause subtle and difficult to trace bugs.

App developers won't be able to have confidence that models they have defined will actually be used as defined.

This is why I would prefer to simply have an empty `User` model that is basically just a PK, which is used only as the glue that connects all the related bits and pieces (auth models, profile models, group and permissions models, etc.)

The rest of the existing `User` fields can be implemented in an `AdminUser` model, and allow multiple authentication backends, which defaults to just an `AdminUserBackend` authentication backend.

Apps and projects can just use this if they like, or they can define whatever models they like to link to the user (if they want to hook into other data that is linked to the user), along with their own auth backend. Then users can login with either admin credentials, or your project/apps credentials. A few abstract user base classes or auth backend classes could help make this part easier.

I'm not convinced that there should even be this concept (like the "data bag") that an app can rely on "some app" ("any app") defining a value for a user related field (e.g. name, email, twitter name, etc.), without knowing or caring which app has defined it. If you don't know which app defines the data, how can you know that it is the correct data for your app and your purpose? Likewise if you change a value, how do you know it won't inadvertently impact another app that relies on it? You won't know what other apps will be installed along side your own, what fields they will define, or when they will want to overwrite those fields.

There is such a thing as too generic. People can and do already define their own user/profile models and authentication backends. They can and do create their own models to store arbitrary data like name, twitter name, longer email address. The only problem is that there are then required and redundant or insufficient vestigial fields left over in `auth.User`. I have my own user model with username field, but I have to fake an `auth.User.username` to connect my user to groups and permissions. I can't use signals to sync my username/email address to `auth.User.email` for the Django admin because it's only 75 characters long and not RFC compliant.

If `auth.User` was just a glue model, with just a PK, and the other fields were moved to `AdminUser` (and `AdminUser.email increased in length), this would be a non-issue because I would not need to even have an `AdminUser` at all to create users for my app/project. I would only need to create one for users of the admin app, and in that case I should adhere to the AdminUser model's field requirements.

Cheers.
Tai.

Luke Sneeringer

da leggere,
4 apr 2012, 09:17:2904/04/12
a django-d...@googlegroups.com
Regarding swappable models:
In general, good reusable applications allow developers to substitute in their own class or subclass for the module author's default by either a custom setting or an argument to a method. In general, there's no reason why that mechanism is insufficient, and should be done per application.

Right now, we're trying to solve the swappable model *for User*, which is a bigger problem because it's not confined to a single app; it's defined in auth, but auth functions as a nexus for lots of other things.

Regards,
Luke

Russell Keith-Magee

da leggere,
4 apr 2012, 10:44:2504/04/12
a django-d...@googlegroups.com

On 04/04/2012, at 8:44 PM, Tai Lee wrote:

> I'm not so sure that it's necessary or even desirable to solve the "general" problem of swappable models. If anyone can swap any model by changing a setting, that sounds like a recipe for confusion to me.

Sure, but that's not what I've proposed. A model would only be swappable if the original app developer declared that model as swappable. An end user wouldn't be able to arbitrarily decide that they wanted to replace a model in an app developed by someone else.

And sure, any feature we add could ultimately end up being used (and overused) in bad ways. However, that's true of any language or library feature. Classes, metaclasses, decorators, or any other Python language feature can be both used and abused, as can Django features like ModelForms or the internals of the Meta class.

My point is that there is nothing about this problem that is unique to User. Django's own codebase contains another example of exactly the same pattern -- Comments. Therefore, we shouldn't pretend that the solution is User specific. At some point, we have to just provide enough documentation and guidance to shepherd people away from bad architectural decisions, and trust that the userbase will take that advice.

Yours,
Russ Magee %-)

Jacob Kaplan-Moss

da leggere,
4 apr 2012, 10:57:5704/04/12
a django-d...@googlegroups.com
On Wednesday, April 4, 2012 at 9:44 AM, Russell Keith-Magee wrote:
My point is that there is nothing about this problem that is unique to User. Django's own codebase contains another example of exactly the same pattern -- Comments.
As the original author and designer of that pattern, I should probably point out that I now think it's a mistake. Have you actually tried using it? It doesn't really work very well. Every time I've introduced any sort of "swappable model" mechanism I've come to regret it.

I'm -1 on the idea of generalized "swappable model" mechanism. I can stomach it for User because in this context it's not any worse than the alternatives, but as a generalized mechanism it makes me really, really unhappy. Decoupling is a laudable goal, but providing a mechanism to reach in and effect code not under your control isn't good design. We use derisive terms like "monkeypatching" for a reason.

I'm sure there are good reasons for wanting swappable models. Russ, I know you're smarter than me, so the fact that you want LazyForeignKey so much probably indicates that you've got some really solid use cases in mind. But really this is a hard veto from me; I just can't be convinced that this sort of mechanism is anything but bad news. 

However, I don't see why we should actively prevent this sort of thing being done externally, so if there's anything in Django that's precluding the creation of a LazyForeignKey as an external add-on let's consider that limitation a bug and get it fixed.

Jacob

Adrian Holovaty

da leggere,
4 apr 2012, 12:20:4504/04/12
a django-d...@googlegroups.com
On Wed, Apr 4, 2012 at 9:57 AM, Jacob Kaplan-Moss <ja...@jacobian.org> wrote:
> On Wednesday, April 4, 2012 at 9:44 AM, Russell Keith-Magee wrote:
>
> My point is that there is nothing about this problem that is unique to User.
> Django's own codebase contains another example of exactly the same pattern
> -- Comments.
>
> As the original author and designer of that pattern, I should probably point
> out that I now think it's a mistake. Have you actually tried using it? It
> doesn't really work very well. Every time I've introduced any sort of
> "swappable model" mechanism I've come to regret it.

Totally agree with Jacob here, plus Tai's comment that "There is such
a thing as too generic." We've made the mistake of making things too
generic in the past, and it's kind of infuriating in retrospect, both
philosophically and in terms of code maintenance/understanding.
(django/utils/tree.py, anyone??)

I think our policy should be: make the simplest thing that can
possibly work for a narrowly-tailored use case, then make things more
generic *slowly* if there's a demand. No need to be an Architecture
Astronaut.

Adrian

Donald Stufft

da leggere,
4 apr 2012, 12:22:3704/04/12
a django-d...@googlegroups.com
Not adding anything, just saying that Architecture Astronaut is the best term ever for this. 

Adrian

Adrian Holovaty

da leggere,
4 apr 2012, 12:26:1304/04/12
a django-d...@googlegroups.com
On Wed, Apr 4, 2012 at 11:22 AM, Donald Stufft <donald...@gmail.com> wrote:
> Not adding anything, just saying that Architecture Astronaut is the best
> term ever for this.

Here's the source of that term:

http://www.joelonsoftware.com/articles/fog0000000018.html
http://www.codinghorror.com/blog/2004/12/it-came-from-planet-architecture.html

Adrian

Daniel Sokolowski

da leggere,
4 apr 2012, 16:28:4104/04/12
a django-d...@googlegroups.com
I agree with Luke that more explicit is better then implicit when dealing with the user.data.
 
Sent: Tuesday, April 03, 2012 2:46 PM
Subject: Re: auth.user refactor: the profile aproach
 

Luke Sneeringer

da leggere,
4 apr 2012, 16:32:0004/04/12
a django-d...@googlegroups.com
I made this in response to Jacob's original proposal, which I understand to have been scrapped in favor of Adrian's (which doesn't have user.data at all, thankfully).
Therefore, I believe my point below is now moot.

For the record, I am much more excited about Adrian's proposal (no offense, Jacob :)). I was convinced that profiles was the only way to go when I read Jacob's proposal, but it sounds like there is a mechanism to make swappable User models work, which is much more in tune with how I use Django. Profiles, to me, are ugly. At best.

Regards,
Luke

Russell Keith-Magee

da leggere,
4 apr 2012, 20:23:2304/04/12
a django-d...@googlegroups.com

On 04/04/2012, at 10:57 PM, Jacob Kaplan-Moss wrote:

> On Wednesday, April 4, 2012 at 9:44 AM, Russell Keith-Magee wrote:
>> My point is that there is nothing about this problem that is unique to User. Django's own codebase contains another example of exactly the same pattern -- Comments.
> As the original author and designer of that pattern, I should probably point out that I now think it's a mistake. Have you actually tried using it? It doesn't really work very well. Every time I've introduced any sort of "swappable model" mechanism I've come to regret it.
>
> I'm -1 on the idea of generalized "swappable model" mechanism. I can stomach it for User because in this context it's not any worse than the alternatives, but as a generalized mechanism it makes me really, really unhappy. Decoupling is a laudable goal, but providing a mechanism to reach in and effect code not under your control isn't good design. We use derisive terms like "monkeypatching" for a reason.

Agreed that monkeypatching is bad. However, I think that's the wrong analogy in this case; I think "interface" is a closer match.

An interface is essentially just a way of saying "I need to operate on an object with these properties, but I'm going to let the end user define the specific object instance that provides those properties". As a language, Python generally doesn't need interfaces because of duck typing. However, ForeignKey is a "strongly typed" database relationship, so you can't duck type it -- the original app developer needs to be able to define it as something that the end developer might want to change.

> I'm sure there are good reasons for wanting swappable models. Russ, I know you're smarter than me,

Don't put too much weight in that argument. I have plenty of braindead ideas. The PhD just means I'm persistent :-)

> so the fact that you want LazyForeignKey so much probably indicates that you've got some really solid use cases in mind. But really this is a hard veto from me; I just can't be convinced that this sort of mechanism is anything but bad news.

To tell the truth, I haven't used the comments app extensively, so I can't really comment on that example specifically. I also don't have a specific case in mind where I want to use this feature. However, if you think of the problem being solved here as interfaces, it isn't hard to think of other possible applications.

More broadly, my reaction comes from the the fact that I *have* been bitten -- many times -- by claims that "this is a special case". Special cases almost never are, and the counterexample usually shows up a week after you're irretrievably committed to a "specialized" solution.

> However, I don't see why we should actively prevent this sort of thing being done externally, so if there's anything in Django that's precluding the creation of a LazyForeignKey as an external add-on let's consider that limitation a bug and get it fixed.

Sure -- I can live with that. There's really only two places where I see this being an issue in practice:

1) Introducing UserField(). I'd be against introducing UserField() as a special case of ForeignKey(). Even though it's more typing, ForeignKey(settings.USER_MODEL) offends me less. This doesn't preclude other developers writing their own ForeignKey shortcuts; I just think it sets a bad example.

2) The synchronization/app cache problem. In order to do this properly, we need to prevent the auth_user table from being created, and auth.User being added to the app cache. If we're going to address this by adding hooks into the model startup process, I'd like to see them as general hooks, rather than special cases for auth.User -- even if they're undocumented hooks.

Of course, the other option is just an if statement in the models.py file:

if settings.USER_MODEL == 'auth.User':
class User(models.Model):
username = ...

which is a pattern that other uses could adopt.

Yours,
Russ Magee %-)


Russell Keith-Magee

da leggere,
4 apr 2012, 20:45:2804/04/12
a django-d...@googlegroups.com

Certainly agreed that astronauting is a bad thing.

However, I would like to draw attention to one particular benefit of the generic solution (other than being generic):

Once we make User swappable, every app with a model that has a ForeignKey to User will need to be updated. This can be done slowly; existing projects that use auth.User will continue to work. However there won't be any real alert to the fact that the app requires updating until you try to swap out User and things start to break. This breakage will be somewhat unpredictable -- you'll just be observing side effect errors as a result of a non-existent (or empty) auth_user table.

Under the approach I described, User will be marked (in the Meta object) as swappable, and ForeignKey(User) will be a hard link to a swapapble model, which is something we can catch as a validation warning, providing a cue to developers that there are apps in their project that may need to be updated. And if there is a ForeignKey(User) and settings.USER_MODEL *isn't* User, then we can raise an validation error, because we know that this app won't work.

Even if we don't implement a fully generic solution, I think this property of the generic approach is worth preserving.

Yours,
Russ Magee %-)

Tai Lee

da leggere,
4 apr 2012, 21:37:2604/04/12
a django-d...@googlegroups.com
Are we really sure that we can or should stomach a swappable User model as a special case?

It's highly likely that I am missing something (or a few things), but aren't the main benefits of swapping the User model just to avoid making a backwards incompatible change, and to avoid making `select_related()` queries?

I'm not suggesting that backwards incompatibility is not important, but I'm not convinced by the argument about inefficient `select_related()` queries, and whatever we do will involve manual schema migration for many users.

Do we really need to allow users to swap in their own model with a combination of additional project level fields as well as additional fields required by various pluggable apps as the primary `User` model, instead of simply stripping out authentication and identifying fields from the primary `User` model and allowing users to implement whatever they need as app or project level profile models?

The more I think about it, the more I think this will just lead to more fragmentation and incompatibilities with pluggable apps, when the user "interface" is abused or causes conflicts.

What's going to happen when this is rolled out and people start developing an eco-system of pluggable apps without a concrete `User` model in Django? All they can rely on is that there will be some form of `User` model and that it *should* provide at least a minimal set of fields and behaviours as specified by some guidelines in Django docs?

Won't we just be back at square 1 in deciding what to include in the minimal fields and guidelines for the this "contract"? Isn't the only sensible minimal set of fields, no fields at all?

Pluggable apps will have to either dictate to developers in the installation instructions that their particular `User` model must have certain fields or behaviours that are required by the pluggable app, which may even conflict with other pluggable apps. Or they will still have to fallback to using an app-profile model, which brings us back to using `select_related()` anyway. I don't like the idea that a pluggable app might require users to change their existing models like this.

If pluggable apps will still need to use app-profile models anyway, if we can get past the backwards incompatibility issue, what is so bad about simply having a `User` model which has no auth or identity data in it, but is just there as glue for for apps to share data for a single "user"?

Bundled apps like the admin would define `AdminProfile` and any pluggable apps that require or support the admin could access `user.admin_profile`. Pluggable apps that don't use the admin and/or have their own auth or identifying requirements can just ship with their own profile and/or auth models and optional auth backend.

The only real issue I have with Django users/auth as it is right now is that there are redundant (for me) or non-compliant (with RFC) fields in `auth.User` that are required and that I have to fake in order to hook into the admin, groups and permissions or re-specify in my own profile for RFC-compliance.

The other problem I have seen mentioned with the profile approach is managing common fields (e.g. two pluggable apps that have a "name" field). I'm quite happy for this to be managed by the developer at a project level, either using signals or forms or a `save()` method on their profile model or whatever else they like to keep that data in sync, if it needs to be kept in sync.

I don't think pluggable apps requiring that developers have a "name" (for example) field on their primary `User` model is really a good solution to this problem, because the pluggable app doesn't know what other purpose that field is used for, and doesn't know if it is changed in app1 if it will have any consequences for app2.

If developers really want a single project level `User` model, they can still create that (with an FK back to Django's `User` model), and simply update it's `save()` method to sync any common fields on all the pluggable app's profile models (which may even have different field names). E.g. `project.User` could have `first_name` and `last_name`, but app1 has only `name`, and app2 has `given_name` and `family_name`. In `project.User.save()`, the developer of a project can determine how to sync this data.

Cheers.
Tai.

bhuztez

da leggere,
4 apr 2012, 23:21:1304/04/12
a Django developers
LazyForeignKey will not solve any problem that can't be solved now,
and we have to introduce a new concept. It sounds like, when we
encounted a hard problem, we give it a new name, instead of trying to
solve it. Moreover, to support multiple user models, each app should
be able to link to a different user model, LazyForeignKey can't be any
help. Maybe the only way to solve this is to add a schema migration
tool. When schema migration tool found a ForeignKey link to a
different model than before, it raises a error, then I know there is
something wrong.


On Apr 5, 8:45 am, Russell Keith-Magee <russ...@keith-magee.com>
wrote:

Daniel Sokolowski

da leggere,
5 apr 2012, 11:29:3605/04/12
a django-d...@googlegroups.com
Can someone give me an example please where a swappable model would benefit
me as opposed to one-to-one model inheritance, FKs, or even proxy models?
Django does this very well and allows
for seamless up/down traversal.

It seems that swappable models are nothing but a different way of doing what
I already am doing for when I am extending the flat pages; except that it
ads a nice centralized SWAPPABLE_MODELS setting.
Furthermore if I do make a swappable custom user model the correct
implementation of 'standard' User methods depends on me; sure I could extend
a the base User model and inherit these methods but then
what did I gained by having this swappable model concept?

The more I think about it the more it makes sense to me to have a base User
model just a stub with a user Identifier and password field. Sure one could
argue for less ore more fields, but I think the idea
is to pick something that would work in 95% situations and yet be flexible
enough to be extended as you wish. Need to extend to add your own
authorization you create a profile with a FK key to the base model and
a new auth backend.

Perhaps the reason the current contib.auth is still not overhauled is
because it does work for a large number of situation and extending it is
relatively easy. Keep the solution simple - I think we are overthinking it.

-----Original Message-----
From: Russell Keith-Magee
Sent: Wednesday, April 04, 2012 10:44 AM
To: django-d...@googlegroups.com
Subject: Re: auth.user refactor: the profile aproach

Yours,
Russ Magee %-)

--

Daniel Sokolowski

da leggere,
5 apr 2012, 12:06:5305/04/12
a django-d...@googlegroups.com
No I can not stomach a swappable User model or the LFK approach, so thank you for voicing your concerns.
 
From: Tai Lee
Sent: Wednesday, April 04, 2012 9:37 PM
Subject: Re: auth.user refactor: the profile aproach
--
You received this message because you are subscribed to the Google Groups "Django developers" group.

Ian Lewis

da leggere,
5 apr 2012, 12:32:1505/04/12
a django-d...@googlegroups.com, django-d...@googlegroups.com
Hi.

iPhoneから送信

On 2012/04/04, at 5:34, Adrian Holovaty <adr...@holovaty.com> wrote:
> First, some background: I haven't used the built-in User module in
> several years. I always write my own User model from scratch -- it's
> so nice and clean. Want a twitter_username field? Just add it. No need
> to add a convoluted foreign key or (oh god) one-to-one relationship to
> some other table.
>
> To me, this is the Right Way to do things. The framework should bend
> to my needs, I shouldn't bend to the framework's needs.
>
> Also, profile modules need to die. They needed to die circa 2006.

Yes, Yes, Yes. I'm really glad someone besides me disliked user profiles and favored just creating your own user model. I was feeling lonely.

Just like Adrian, me and everyone at my company have used our own user models for years. It always felt like the simplest and most straight-forward approach. It also has the be benefit that users can use south or anything they want. For migrating and managing user data. Big big +1.

> So, with that in mind, I've got to say I prefer Alex's solution. I
> really think the right way to do it is:
>
> 1. Let you create your own User model, with whichever fields you want.
>
> 2. Provide a way to tell Django which model you're using for that.
>
> 3. In your own code, just deal with that model like you deal with any
> other one. No need to jump through hoops.
>

> 4. Third-party models should be changed to use something like "user =
> UserField()", which would automatically create a foreign key to the
> registered User model. If you change your registered User model after
> you've created those third-party tables, you're in for trouble. Don't
> do that.
>
> 5. Django provides some generic APIs for getting at the registered
> user. Example: a middleware that sets request.user based on the
> current session.

I wanted to point out that Adrien's plan is pretty close to newauth. Though I didn't go so far as to provide a UserField, I did have a way for third party apps to get a handle of the User model via a get_user_model() function. The UserField would just glue something like that to a ForeignKey.

> 6. Given that some third-party apps will likely want to get access to
> common attributes of a User -- notably, email address -- there could
> be some sort of standard interface that User models need to adhere to
> (duck typing). So, a custom User model would say "for this User model,
> the email address is stored in the database field called 'email'" --
> or "this User model doesn't have email addresses.

I think people get too hung up on providing fields do third party apps when you could provide a minimal set of methods which subclasses could override. There isn't really any reason i can think of that third party apps need to access the fields directly.

Though if you really hav to deal with fields, I prefer a simple convention for common fields; for example, 'email' for the email field. That way you can do a simple hasattr() to look if the model has a particular field. I realize convention is hard to document though.

I'm curious though how the admin fits into your ideas. If you wanted to use the admin, would you have to use a User model that has and or supports all the cruft on the current user model? e.g. username, password, email, permissions etc.

All in all I'm excited about where this is going and that you're taking the lead on this. Can't thank you enough.

Adrian Holovaty

da leggere,
5 apr 2012, 13:02:1805/04/12
a django-d...@googlegroups.com
2012/4/5 Ian Lewis <ianm...@gmail.com>:

> I'm curious though how the admin fits into your ideas. If you wanted to use the
> admin, would you have to use a User model that has and or supports all the
> cruft on the current user model? e.g. username, password, email, permissions
> etc.

Yes, you'd need to tell the admin how your User model interacted with
it -- how it authenticates, how permissions work, etc.

Adrian

Alex Gaynor

da leggere,
5 apr 2012, 13:04:4805/04/12
a django-d...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.


I haven't been following this thread nearly closely enough.  But ISTM that any abstraction that doesn't let the admin work with any User (assuming it supplies the right interface) isn't very useful, and rather misses the point.

Alex

--
"I disapprove of what you say, but I will defend to the death your right to say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero

Ian Lewis

da leggere,
5 apr 2012, 13:09:3805/04/12
a django-d...@googlegroups.com, <django-developers@googlegroups.com>
Hi

iPhoneから送信

On 2012/04/06, at 0:29, "Daniel Sokolowski" <daniel.s...@klinsight.com> wrote:
> The more I think about it the more it makes sense to me to have a base User model just a stub with a user Identifier and password field. Sure one could argue for less ore more fields, but I think the idea
> is to pick something that would work in 95% situations and yet be flexible enough to be extended as you wish. Need to extend to add your own authorization you create a profile with a FK key to the base model and
> a new auth backend.

The good part about swappable user models is that you don't need to necessarily fix the model's DB fields in advance. Your identifier and password's length and other properties can be user defined and can be reflected in the DB.

Django can also leave migration of user models an data up to the developer so they an use south or whatever to manage the user model since they "own" it. Django Devs wouldn't necessarily need to support a DB table that can essentially never change as is the case currently.

Fixing that stuff ahead of time and just making a FK means the user model itself is fixed. I don't want to be able to just add fields. I want to be able to change the primary key to be a uuid, or a email, or use the default auto incrementing integer. I want to be able to use an existing model or DB table and, with some work, plug it into Django auth.

Now if I'm customizing the user model itself anyway, why not just tack on whatever other fields I want? I don't need a FK since those fields are on the user model and I can create however many one to one or one to many relationships for "profiles" or user data as makes sense for my project. That may be a lot or it may be zero.

(now whether that is realistic given the needs of the admin is a different story)


> Perhaps the reason the current contib.auth is still not overhauled is because it does work for a large number of situation and extending it is relatively easy. Keep the solution simple - I think we are overthinking it.

Sure. I'm not completely discontent continuing to use my own library and simply shun anything that uses auth (besides the admin maybe), but I think Django would be better off with a better auth module.

Tai Lee

da leggere,
5 apr 2012, 16:29:0805/04/12
a django-d...@googlegroups.com

On 06/04/2012, at 3:09 AM, Ian Lewis wrote:

> The good part about swappable user models is that you don't need to necessarily fix the model's DB fields in advance. Your identifier and password's length and other properties can be user defined and can be reflected in the DB.
>
> Django can also leave migration of user models an data up to the developer so they an use south or whatever to manage the user model since they "own" it. Django Devs wouldn't necessarily need to support a DB table that can essentially never change as is the case currently.
>
> Fixing that stuff ahead of time and just making a FK means the user model itself is fixed. I don't want to be able to just add fields. I want to be able to change the primary key to be a uuid, or a email, or use the default auto incrementing integer. I want to be able to use an existing model or DB table and, with some work, plug it into Django auth.
>
> Now if I'm customizing the user model itself anyway, why not just tack on whatever other fields I want? I don't need a FK since those fields are on the user model and I can create however many one to one or one to many relationships for "profiles" or user data as makes sense for my project. That may be a lot or it may be zero.
>
> (now whether that is realistic given the needs of the admin is a different story)

But I still don't see how a swapped in `User` model which *has* to behave in a specified way so that it can work with the Django admin and any other pluggable apps that might have special requirements, is any better than simply allowing the admin and other pluggable apps to have their profile and authentication needs self-contained?

If Django's `User` model was just a stub (without even username and password fields), and Django shipped with an abstract `BaseAuth` model with a `username` field that was email compliant and a `password` field, and corresponding `BaseAuthForm` and `BaseAuthBackend`, then user's can still create their own `User` model with literally *whatever* fields they want in it, they can use the standard auth fields, form and backend provided by Django, or roll their own.

Instead of creating a custom `User` model that quacks like an admin duck, and quacks like every pluggable app that is installed as well, all they need to do is create/update an an `AdminUser` whenever their custom `User` is saved.

This is explicit, the admin and other pluggable apps know where to access information that they need (from their own models), and the developer has control over how the data is kept in sync across the pluggable apps used in the project, at the project level.

Cheers.
Tai.

Anssi Kääriäinen

da leggere,
5 apr 2012, 17:42:1305/04/12
a Django developers
On Apr 5, 11:29 pm, Tai Lee <real.hu...@mrmachine.net> wrote:
> But I still don't see how a swapped in `User` model which *has* to behave in a specified way so that it can work with the Django admin and any other pluggable apps that might have special requirements, is any better than simply allowing the admin and other pluggable apps to have their profile and authentication needs self-contained?
>
> If Django's `User` model was just a stub (without even username and password fields), and Django shipped with an abstract `BaseAuth` model with a `username` field that was email compliant and a `password` field, and corresponding `BaseAuthForm` and `BaseAuthBackend`, then user's can still create their own `User` model with literally *whatever* fields they want in it, they can use the standard auth fields, form and backend provided by Django, or roll their own.
>
> Instead of creating a custom `User` model that quacks like an admin duck, and quacks like every pluggable app that is installed as well, all they need to do is create/update an an `AdminUser` whenever their custom `User` is saved.
>
> This is explicit, the admin and other pluggable apps know where to access information that they need (from their own models), and the developer has control over how the data is kept in sync across the pluggable apps used in the project, at the project level.

If every application provides a profile which matches it needs, you
will get a serious case of data-duplication. Every application which
needs email-address should then create a profile containing the email.
Another way to solve this would be to check the existing profiles for
the email field, and now you have introduced some magic, and in
addition you have mostly just moved the interface contract from the
User model to its profile. So, you have the same problem again. This
is the failing of the profile based approach: you don't actually solve
anything by it, you just move the problem around (and introduce more
problems by doing that). (Yes, I was +1 on the idea. Opinions
change...)

I guess 90% of users will either use the BackwardsCompatibilityUser,
or the new shiny FixedUser. For most users they are more than
adequate. The issue is only when you really need something completely
different for one reason or another. In addition with proxy models and
custom managers you can have pretty good profile based approach in the
cases where you need it. It is a good approach if you have
TwitterUser, FaceBookUser, GooglePlusUser and so on. (Or CustomerUser,
EmployeeUser and so on).

I bet one of the hardest problems will be how to define the ORM
interface, and how custom classes can fulfill
that. .filter(is_admin=True) can only work if there is a is_admin
field. I guess everything will need to go through manager methods, and
the "must implement" set must be kept at minimum.

- Anssi

Jacob Kaplan-Moss

da leggere,
5 apr 2012, 19:27:4305/04/12
a django-d...@googlegroups.com
On Thursday, April 5, 2012 at 12:04 PM, Alex Gaynor wrote:
I haven't been following this thread nearly closely enough.  But ISTM that any abstraction that doesn't let the admin work with any User (assuming it supplies the right interface) isn't very useful, and rather misses the point.


I'm starting to come to the conclusion that as part of this process we'll need to document a sort of "minimal viable user" -- the smallest set of user APIs that a reusable app -- including the admin -- can reasonably rely on. If we're fairly clear about this interface then it remains fairly easy to produce apps that rely on auth in an abstract way. In fact, this sort of "minimal user" might even make auth-dependant reusable apps *more* prevalent since at the moment the set of user APIs are rather nebulous and ill-specified. 

Jacob

Russell Keith-Magee

da leggere,
5 apr 2012, 21:45:4005/04/12
a django-d...@googlegroups.com

It's a good thing that I didn't document that as a "universal concern" on the wiki right from the start ...

https://code.djangoproject.com/wiki/ContribAuthImprovements#TheUserContract

Oh wait...

:-)

Russ %-)

Tai Lee

da leggere,
5 apr 2012, 21:52:1305/04/12
a django-d...@googlegroups.com
Thanks Anssi, but I think the interface contract for a pluggable app *should* be on the pluggable app's profile, instead of relying on an assumption that developers will have created a user model that is duck typed to suit every pluggable app installed in the project (which might not be practical or even possible).

For the sake of avoiding duplication, which could be managed with signals and the save() method of a project level profile, you potentially overload fields that are not analogous across pluggable apps by sharing a single namespace. E.g. is_active for the admin may not mean is_active for app2 or app3.

Jacob, by having to define a minimum set of fields that users must implement in their swappable User model to support contrib apps like the admin, don't we end up exactly where we are now? Isn't this exactly what we have already?

The only difference I see then is that the interface would be defined in docs instead of a model and re-implemented by developers for every project, and pluggable apps will often require a schema migration on the swapped-in project level User model when adding a pluggable app to an established project.

In my ideal scenario, User would be a glue model with just a PK, and maybe some convenience methods. AdminProfile would not have username or password fields, but would have is_active, is_superuser, and maybe optionally a name, etc.

Developers would need to create at least one model in their project, which would be a subclass of BaseAuthModel in most cases, which stores authentication credentials.

They would also need to create an auth backend, which would be a subclass of BaseAuthBackend in most cases.

Pluggable apps could also define auth models and backends if they require specific auth to function (e.g. an app that adds twitter integration.)

Then people could login to the admin and any other pluggable apps using whatever credentials and authentication system they like (username/email and password, twitter, Facebook, openid, etc.) Developers wouldn't need to implement a minimal set of fields or do a database schema migration to support or install any pluggable apps.

If app1 or app2 require access to an email address (for example), their profiles should have a required email field. It should be up to the project developer to make sure that an email address is set when creating an app1 or app2 profile for a User, and synchronizing them between app1 and app2 (if appropriate).

I think the data duplication issue is much easier for project developers to manage explicitly without resorting to magic and assumptions than sharing or combining namespaces for profile data, doing database scheme migrations, and duck typing a single model for use with multiple pluggable apps.

Cheers.
Tai.

bhuztez

da leggere,
6 apr 2012, 00:59:4806/04/12
a Django developers
I think we need three different kinds of models, Identity, Profile and
Authentication Info.

* The Identity model should have all fields shared by many apps.
* Each app can have its own Profile model. If a field is not shared
with other apps, just put it in the Profile model.
* Since password is never used after an user is authenticated,
authentication backends should declare fields like passwords, OpenID
credentials in their own Authentication Info models.

Having any default fields in the Identity model is not appropriate,
most likely we will find a use case in which a certain field is not
needed. But if the Identity model has only PK field, data
synchronization will be a big problem, for each field shared by
multiple apps, you need a signal, and suppose you have 20 models and
they have 3 fields in common, when you changed these 3 fields in one
object, you will have another 19 objects saved three times each.

Furthurmore, a project may need more than one Identity model. Both
LazyForeignKey and multiple Profile solution do not address this
problem. Since any app may have its own APP_IDENTITY_MODEL setting,
the unpredictable problem if APP_IDENTITY_MODEL changed after initial
syncdb, is unavoidable. IMO, this problem can only be solved schema
migration tool and should be solved by schema migration tool. If
Django ORM assumes that schema would be changed overtime, schema
migration should not be an optional part of it.




On Apr 6, 9:52 am, Tai Lee <real.hu...@mrmachine.net> wrote:
> Thanks Anssi, but I think the interface contract for a pluggable app *should* be on the pluggable app's profile, instead of relying on an assumption that developers will have created a user model that is duck typed to suit every pluggable app installed in the project (which might not be practical or even possible).
>
> For the sake of avoiding duplication, which could be managed with signals and the save() method of a project level profile, you potentially overload fields that are not analogous across pluggable apps by sharing a single namespace. E.g. is_active for the admin may not mean is_active for app2 or app3.
>
> Jacob, by having to define a minimum set of fields that users must implement in their swappable User model to support contrib apps like the admin, don't we end up exactly where we are now? Isn't this exactly what we have already?
>
> The only difference I see then is that the interface would be defined in docs instead of a model and re-implemented by developers for every project, and pluggable apps will often require a schema migration on the swapped-in project level User model when adding a pluggable app to an established project.
>
> In my ideal scenario, User would be a glue model with just a PK, and maybe some convenience methods. AdminProfile would not have username or password fields, but would have is_active, is_superuser, and maybe optionally a name, etc.
>
> Developers would need to create at least one model in their project, which would be a subclass of BaseAuthModel in most cases, which stores authentication credentials.
>
> They would also need to create an auth backend, which would be a subclass of BaseAuthBackend in most cases.
>
> Pluggable apps could also define auth models and backends if they require specific auth to function (e.g. an app that adds twitter integration.)
>
> Then people could login to the admin and any other pluggable apps using whatever credentials and authentication system they like (username/email and password, twitter, Facebook, openid, etc.) Developers wouldn't need to implement a minimal set of fields or do a database schema migration to support or install any pluggable apps.
>
> If app1 or app2 require access to an email address (for example), their profiles should have a required email field. It should be up to the project developer to make sure that an email address is set when creating an app1 or app2 profile for a User, and synchronizing them between app1 and app2 (if appropriate).
>
> I think the data duplication issue is much easier for project developers to manage explicitly without resorting to magic and assumptions than sharing or combining namespaces for profile data, doing database scheme migrations, and duck typing a single model for use with multiple pluggable apps.
>
> Cheers.
> Tai.
>

Harris Lapiroff

da leggere,
6 apr 2012, 01:18:2906/04/12
a django-d...@googlegroups.com
I very much share Tai's concerns about the swappable user model introducing incompatibilities. Imagine two apps, each of which requires an "age" attribute on the user model. But suppose one of those apps expects age to be the number of years since that user's birth and one of those apps expects the age to be the number of years since the user registered for the website. The user model must provide the same attribute to both apps, but it is supposed to have a different value for each app. A developer will be unable to use these two apps together without patching one of them.

A bit of a contrived example, maybe, but I can imagine this same-name-different-purpose issue coming up over and over again, making otherwise pluggable apps incompatible with each other.

I think we should go with a pared down user model and allow each app to manage whatever data it needs on each user through profiles and signals. Developers will end up with some data duplication, but I think that is preferable to confusion about the source and purpose of data. Profiles are essentially a way for each app to namespace its own data and I think that's a good thing.

Harris

Donald Stufft

da leggere,
6 apr 2012, 01:21:5306/04/12
a django-d...@googlegroups.com
Nothing about this proposal prevents this.

And in that case, no those 2 apps would not be able to be used together. But this is hardly the first
time that 2 apps cannot be used together. because of choices made like that on the app owner.
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/p4jhylEp3x8J.

Alex Ogier

da leggere,
6 apr 2012, 01:44:5106/04/12
a django-d...@googlegroups.com

I think this proposal will make more sense if people stop thinking "If someone wants to use contrib.auth, then why do we need another crufty interface to swap out auth.User?" Instead think of someone who wants to use contrib.admin but not be stuck with contrib.auth.

The point of this proposal isn't to make contrib.auth larger and better, it's to make it unnecessary. A lot of people find contrib.auth's models unsatisfactory. Adrian Holovaty stated that he hasn't used auth.User for several years. When you have a complex site with multiple authentication methods and requirements that don't fit django's idea of a user, it stops being worth it to bend yourself to django's will. The problem is that contrib.auth and contrib.admin are currently intimately linked. This proposal's purpose is to give an official way to break these two apart.

I don't know of a single framework out there besides Django that ships with a fixed model for its users. The reason is that authentication and identity are radically different for every site so it's tremendously important to support whatever people decide to do, and not force decisions on them.

So, stop thinking just in terms of contrib.auth.models.User. If you're already using that with a profile and it's all working fine, then this change isn't for you. This is for everyone who just wishes auth.User would go away without totally borking admin.

Respectfully,
Alex Ogier

Harris Lapiroff

da leggere,
6 apr 2012, 02:04:5306/04/12
a django-d...@googlegroups.com

I don't know of a single framework out there besides Django that ships with a fixed model for its users.

I totally understand this, but I also think the fact that Django *does* ship with this stuff creates a baseline security and quality for logging into most Django-powered web apps. I've seen a fair few really crappy, insecure Rails implementations of user systems...

I'm just wary of apps being able to require non-standard interfaces for the user model. (And if apps are not going to require non-standard interfaces, then I don't really understand why profiles are inadequate for extra user data.) However, if this really isn't going to change things for people who are using contrib.auth.models.User, then I suppose I can't object too much.

Harris

Tai Lee

da leggere,
6 apr 2012, 06:15:2806/04/12
a django-d...@googlegroups.com
Alex Ogier,

Is it really better to require users to create their own User model that behaves like an admin user, instead of just shipping with a self contained admin user (as a profile model) without the auth component?

If the auth app was purely a stub to connect different profiles and authentication systems from different apps (or the project), but doesn't actually define any identity or authentication or profile models itself (not counting base abstract classes), isn't that effectively achieving the separation that you want? Being able to use admin without the current cruft in auth or with any completely different authentication credentials, and similarly using any other pluggable app without any cruft needed by the admin.

In any case, I don't think it will actually be possible to use the admin or any other pluggable app that relies on the concept of a central user who might access to multiple apps (instead of every app having its own users and auth) without *an* auth app and a central User model.

Like Adrian, I don't actually use the User model for auth or identity (name, email, etc.) anymore. But unless you have authored *all* the apps in your project and they know how to talk to *your* User model, you still need a User from Django, because that is what all 3rd party pluggable apps will need.

If I want to use any 3rd party apps that use the central user, I will still need to create a Django User and fake or sync the that are only there for the admin, even if I don't use the admin. If you still require swapped in User models to assume a minimal interface and fields, people will still have this problem.

This is why I think the central User should not contain any auth or identity data, so there is no cruft required only for apps you may not even be using, and so you are not tied to any particular auth system.

Cheers.
Tai.

Alex Ogier

da leggere,
6 apr 2012, 14:31:1306/04/12
a django-d...@googlegroups.com
Tai, read https://gist.github.com/2289395 for a summary of many reasons why I think profiles are a bad idea, and unifying multiple profiles is an even worse idea.

Best,
Alex Ogier

Daniel Sokolowski

da leggere,
6 apr 2012, 15:29:0506/04/12
a django-d...@googlegroups.com
How is the final approach chosen ?
 
From: Alex Ogier
Sent: Friday, April 06, 2012 2:31 PM
Subject: Re: auth.user refactor: the profile aproach
 
Alex Ogier

To unsubscribe from this group, send email to mailto:django-developers%2Bunsu...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to mailto:django-developers%2Bunsu...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Jacob Kaplan-Moss

da leggere,
6 apr 2012, 16:35:5506/04/12
a django-d...@googlegroups.com
On Friday, April 6, 2012 at 2:29 PM, Daniel Sokolowski wrote:
How is the final approach chosen ?
Adrian made the call earlier this week - we're going with Alex's approach.

Jacob

Florian Apolloner

da leggere,
7 apr 2012, 06:25:1007/04/12
a django-d...@googlegroups.com
Hi Adrian,

On Tuesday, April 3, 2012 10:34:18 PM UTC+2, Adrian Holovaty wrote:

I chatted about this with Jacob on IRC, and we reached consensus on
this approach. I'd like to get moving on this and would be happy to
take it on myself, starting next week.

I'd like to help out there, could you please open a new thread with info for the branch etc when you start? Plus it would be great if you'd be in IRC more often, makes some discussions easier than over mail…

Cheers,
Florian
 

Luke Sneeringer

da leggere,
7 apr 2012, 11:54:1407/04/12
a django-d...@googlegroups.com
More generally, what is the general mechanism for coordinating this? I'd like to help as well, but I don't really know how to get started.

Regards,
Luke

Tom Evans

da leggere,
10 apr 2012, 05:47:4110/04/12
a django-d...@googlegroups.com
On Fri, Apr 6, 2012 at 7:31 PM, Alex Ogier <alex....@gmail.com> wrote:
> Tai, read https://gist.github.com/2289395 for a summary of many reasons why
> I think profiles are a bad idea, and unifying multiple profiles is an even
> worse idea.
>
> Best,
> Alex Ogier

Hi Alex

Is https://gist.github.com/2289395 the complete proposal for what is
to be implemented? It seems more of a point by point rebuttal of
another proposal.

Is the approved solution to have mixins which contribute to a user
class? Are pluggable apps expected to provide mixins that contribute
to the user model?

Does this proposal fix the current issues with the user model in new projects?

My biggest concerns with this approach:

1) Monolithic user tables.

Adding apps that want their own user storage with this system requires
new columns in your user model. This is not the best approach. Foreign
keys to separate user data tables per app is much more appropriate.

Monolithic tables kill performance. As an example, a C++ ORM I have
used had this approach, and it was common to come across a user table
with 100+ columns, including some massive fields (unbounded text
fields, blobs). On most page requests, only a few columns were looked
at, but every page paid the cost of retrieving massive user objects.

The most common complaint against profiles is that they are 'slow' as
you have to join to many tables, or issue multiple queries. Queries
against monolithic tables are much slower than a few queries on much
smaller tables.

2) Constant flux on my user model as a site develops

Adding new apps which require user storage would require schema
change. If you have lots of users, this is a real pain. When adding an
app profile, all that is required is a new table to be created, which
would not lock a critical table like 'user' for a long period.


I've no objection to allowing project managers more control over the
user model, but I don't think we should encourage the majority of
pluggable apps to pollute the user model - in fact, probably only apps
dealing with AAA.

Eg, a photo gallery app may want to store the preferred thumbnail size
and whether to open images in a new window. These do not belong on the
user model, but on a photo gallery app profile. Most users may not
have preferences, and for those that do, it is only relevant to access
that info on a photo gallery page.

A twitter auth app may want to store an oauth token. This should
belong on the user model, as it is used for AAA, and may be accessed
on any request.

Cheers

Tom

Tai Lee

da leggere,
10 apr 2012, 06:20:5810/04/12
a django-d...@googlegroups.com
Tom,

Thanks for raising those issues. I would just like to add that I hope to see fields currently in `User` that are required by the admin (is_staff, etc.), moved to an admin profile model, and not simply made available through mixins that are still required by every `User` model that gets swapped in.

Even if we technically allow the `User` model to be swapped in, and therefore allow new fields to be added to it, I think that this would set a dangerous example and precedent for authors of other pluggable apps.

The admin is arguably *the* premiere example of a "pluggable" app, held up as a reference on how it should be done. I think it's important that we clearly define when it is appropriate for a pluggable app to require schema changes to a project's `User` model, and that should only be in rare and specific circumstances.

Generally, fields should only be added to the `User` model by a project author (not a pluggable app author), because pluggable app authors won't know the details of the environment (other pluggable apps and project) they will be installed into.

I also hope to see a more fleshed out proposal from Adrian, before an actual implementation is delivered, hopefully containing answers to some of the questions raised here.

Cheers.
Tai.

Ian Lewis

da leggere,
10 apr 2012, 10:13:0310/04/12
a django-d...@googlegroups.com

Hi,

I'm not getting why you *have* to add fields to the User model to store data pertaining to the user. There is nothing in the proposal for pluggable user models that says you can never have a seperate model with a foreign key to the user model. It just means that you can define your user model the way you want it to be.

Why can't third party apps have a model with a foreign key to the user table with the pluggable models approach? I imagine you are right that every app and it's brother adding fields to the user model is not realistic but I don't think that anyone has proposed that. Certainly not me. The thing I want to be able to is define user models suitable for my project. Third party apps adding their own fields wasn't proposed by anyone AFAIK, nor was specifically requiring that you add them yourself. Some might require that your user has something like an 'email' field because that would be a common field across apps but app specific data can easily go on a seperate model included with the app that simply has a FK to user. You can then only fetch that data on requests that need it.

I'm sorry but doing a JOIN every request is a BAD idea. You will run into problems there quickly and have no way out of it besides ditching auth completely (and thus all the thirdparty apps you use that depend on it). Assuming the user table and profile tables are small is awfully short sighted.

Ian

2012/04/10 18:47 "Tom Evans" <teva...@googlemail.com>:

Tom Evans

da leggere,
10 apr 2012, 10:58:3710/04/12
a django-d...@googlegroups.com
On Tue, Apr 10, 2012 at 3:13 PM, Ian Lewis <ianm...@gmail.com> wrote:
> Hi,
>
> I'm not getting why you *have* to add fields to the User model to store data
> pertaining to the user. There is nothing in the proposal for pluggable user
> models that says you can never have a seperate model with a foreign key to
> the user model. It just means that you can define your user model the way
> you want it to be.

That is perfectly fine. The problem comes when there is a simple
system to add fields to the user model, people will use it to add
fields to the user model in their pluggable apps, for 'simplicity' and
'ease of use'.

> Why can't third party apps have a model with a foreign key to the user table
> with the pluggable models approach? I imagine you are right that every app
> and it's brother adding fields to the user model is not realistic but I
> don't think that anyone has proposed that. Certainly not me.

The proposed solution as decided by BDFL diktat is 2a from [1]. I quote:

Split off as much as possible of auth.User into orthogonal mixins that
can be reused.
Modify auth.User to inherit these mixins. Care must be taken to ensure
that the database expression of the new User model is identical to the
old User model, to ensure backwards compatibility.
Unrelated and third-party apps can indicate that they depend on
various orthogonal mixins. For example, contrib.admin can specify that
it works with auth.User out of the box, and with any model
implementing PermissionsMixin if you supply your own login forms.

At the moment, you cannot change the user model, so we do not have
issues relating to third party apps changing the user model. With the
proposed solution, you would be able to change the user model, so we
may have issues.

It's also enlightening to read the code from Alex's Django branch,
which is an initial implementation of option 2a.

> The thing I
> want to be able to is define user models suitable for my project. Third
> party apps adding their own fields wasn't proposed by anyone AFAIK, nor was
> specifically requiring that you add them yourself. Some might require that
> your user has something like an 'email' field because that would be a common
> field across apps but app specific data can easily go on a seperate model
> included with the app that simply has a FK to user. You can then only fetch
> that data on requests that need it.
>
> I'm sorry but doing a JOIN every request is a BAD idea. You will run into
> problems there quickly and have no way out of it besides ditching auth
> completely (and thus all the thirdparty apps you use that depend on it).

I completely disagree, but I'm not here to try and convince people how
to design their databases. A JOIN every request will not end the
world. Besides, it is far more likely to be a separate query than a
JOIN, and would only happen on views that required that data.

More to the point, what basis are you making this claim on? People
love to pipe up "JOINs are slow and evil", but have you actually
analysed the cost compared to monolithic tables?

> Assuming the user table and profile tables are small is awfully short
> sighted.

To be fair, I was slightly ambiguous with my use of the word 'small'. I said:

>> Queries against monolithic tables are much slower than a few queries on much
>> smaller tables.

Here 'small' means fewer columns, not less tuples.

However, assuming tables will be small is precisely what you have just
done - "we must not have JOINs, they are evil, but it doesn't matter
because the user table will only have the columns I desire". I agree
that it is a short sighted position, if you do not prevent the table
becoming monolithic ;)


Cheers

Tom

[1] https://code.djangoproject.com/wiki/ContribAuthImprovements

Max Thayer

da leggere,
10 apr 2012, 11:04:1710/04/12
a django-d...@googlegroups.com
Hello!

The page you linked, Tom, concludes with this line: "Discussion on django-developers suggests that complete consensus is unlikely; Currently awaiting BDFL mandate."

Since we have the BDFL mandate, shouldn't we update the page?

Best regards,
Max

Alex Ogier

da leggere,
10 apr 2012, 13:04:5010/04/12
a django-d...@googlegroups.com

Hi Tom,

The best rounded description with pros and cons is Solution 2a on https://code.djangoproject.com/wiki/ContribAuthImprovements

You are correct that I am primarily thinking of pluggable authentication when I think of this new user model. The reason is that there is nothing stopping you from continuing to place app data outside the user model as has been standard for a while now. For example, there is nothing stopping you from using the following pattern in you app's view:

if request.user.is_authenticated():
    try:
        prefs = GalleryPref.objects.get(user=request.user)
    except GalleryPref.DoesNotExist:
        prefs = None

That is, unless you have a reason that your particular data should be eagerly loaded on every request there is no reason to require it on the user. In fact app developers are incentivized to keep their data separate in order to remain compatible with the default user.

The solution isn't perfect, it does in fact provide some barriers to this pattern. The Gallery app must explicitly choose to foreign key to settings.USER_MODEL, and once they do *changing* which model the setting points to requires a migration of your table. These are both real issues, but I don't think that user model bloat will be because there is a straightforward way to work around it if it does prove to be an issue for any particular project.

The only thing this proposal kills is magic proxying back from user attributes. If you really wanted to, you could roll your own proxy attributes to app fields, after all you control the entire user class.

Best,
Alex Ogier

Alex Ogier

da leggere,
10 apr 2012, 13:25:4110/04/12
a django-d...@googlegroups.com

Tom,

I proposed mixins to solve the specific problem: there is an app that needs a specific contract from a model it wants to authenticate or otherwise interact with, how can we make it easy for developers to implement that contract?

Most apps don't actually need that much though. There are a bunch of standard ways to relate to a model that don't invasively change it. They are all still available, and in fact preferred because no matter how easy it is to use a mixin, doing nothing is even easier.

Best,
Alex Ogier

Tai Lee

da leggere,
10 apr 2012, 18:01:3310/04/12
a django-d...@googlegroups.com
Alex,

I think the problem with this aspect of your proposal is that it signals a green light for other pluggable apps to follow Django's lead and provide mixing which must be added to their `User` model in order to use the pluggable app, instead of creating a profile model for their pluggable app.

Django's admin is a pluggable app, and it should follow the best practices that we recommend for authors of other pluggable apps.

It has been suggested that "nothing is stopping pluggable app authors from continuing to use profiles", but on the flip side, nothing is going to stop them from using mixins and requiring a schema migration to install their pluggable app.

If we allow swappable models, we should strongly recommend (in documentation, and by example with the admin) that pluggable app authors continue to use profiles, and that only project authors use the ability to swap in a new `User` model for their own purposes, and not to support a pluggable app.

Cheers.
Tai.

Donald Stufft

da leggere,
10 apr 2012, 18:05:3410/04/12
a django-d...@googlegroups.com
I think swappable user models should be used as a replacement for get_profile() not per app profiles.

It should be used for generic-esque data about a User. e.g. Email, phone number, name, etc.

It should not be used for app specific data about a user, e.g. Default Gallery, Notification Settings, etc.

Tai Lee

da leggere,
10 apr 2012, 18:34:0410/04/12
a django-d...@googlegroups.com
Sure. But will how will users be guided to making that distinction when developing their pluggable apps? I haven't seen anything that would prevent developers from doing this, and if the admin itself does it, isn't that a green light for everyone to do it?

Another problem with pluggable apps (like the admin) adding fields to the `User` model is that they make an assumption that *every* user is a user of that particular pluggable app.

I use the admin, but not *every* user has access to the admin. Those users shouldn't need to have values (even default values, e.g. is_staff=False) for admin-specific fields, and admin-specific fields shouldn't be selected every time any user is retrieved from the database.

Cheers.
Tai.

Donald Stufft

da leggere,
10 apr 2012, 18:36:4310/04/12
a django-d...@googlegroups.com
Sorry I was mentioning that in agreement with documenting this. I had even toyed with the idea of making the default template provide a project level user model by default (to cement the fact that it's project concern not a app concern) but that is most likely more trouble than it's worth.

Ian Lewis

da leggere,
10 apr 2012, 21:08:3810/04/12
a django-d...@googlegroups.com
On Tue, Apr 10, 2012 at 11:58 PM, Tom Evans <teva...@googlemail.com> wrote:
On Tue, Apr 10, 2012 at 3:13 PM, Ian Lewis <ianm...@gmail.com> wrote:
> Hi,
>
> I'm not getting why you *have* to add fields to the User model to store data
> pertaining to the user. There is nothing in the proposal for pluggable user
> models that says you can never have a seperate model with a foreign key to
> the user model. It just means that you can define your user model the way
> you want it to be.

That is perfectly fine. The problem comes when there is a simple
system to add fields to the user model, people will use it to add
fields to the user model in their pluggable apps, for 'simplicity' and
'ease of use'.

Um. yeah. that's kind of the point. Third party apps being able to do so is not good but me being able to do so is really really great. No proposal that I've seen allows third party apps to add fields willy-nilly so what is the problem here?
 

> Why can't third party apps have a model with a foreign key to the user table
> with the pluggable models approach? I imagine you are right that every app
> and it's brother adding fields to the user model is not realistic but I
> don't think that anyone has proposed that. Certainly not me.

The proposed solution as decided by BDFL diktat is 2a from [1]. I quote:

Split off as much as possible of auth.User into orthogonal mixins that
can be reused.
Modify auth.User to inherit these mixins. Care must be taken to ensure
that the database expression of the new User model is identical to the
old User model, to ensure backwards compatibility.
Unrelated and third-party apps can indicate that they depend on
various orthogonal mixins. For example, contrib.admin can specify that
it works with auth.User out of the box, and with any model
implementing PermissionsMixin if you supply your own login forms.

At the moment, you cannot change the user model, so we do not have
issues relating to third party apps changing the user model. With the
proposed solution, you would be able to change the user model, so we
may have issues.

Yes. Third party apps changing the user model is bad. I agree. You should need to explicitly add
any field the third party app needs. Preferably these would be common things like 'email' or the user's name.
Any other app specific data would go in models provided by the app with an FK to user.

In other words, an app providing a user model mixin that YOU add to your user model is OK, though probably bad design.
For third party app related data, it makes sense to have it in a separate table. I imagined that that mixins would be there
so that you could choose to add or not add the fields to support the admin, but apps providing mixins to add fields
to the user as a normal design is probably a bad idea. I see where I may have been confused.

It's also enlightening to read the code from Alex's Django branch,
which is an initial implementation of option 2a.

I haven't seen that so I can't comment.
 
> The thing I
> want to be able to is define user models suitable for my project. Third
> party apps adding their own fields wasn't proposed by anyone AFAIK, nor was
> specifically requiring that you add them yourself. Some might require that
> your user has something like an 'email' field because that would be a common
> field across apps but app specific data can easily go on a seperate model
> included with the app that simply has a FK to user. You can then only fetch
> that data on requests that need it.
>
> I'm sorry but doing a JOIN every request is a BAD idea. You will run into
> problems there quickly and have no way out of it besides ditching auth
> completely (and thus all the thirdparty apps you use that depend on it).

I completely disagree, but I'm not here to try and convince people how
to design their databases. A JOIN every request will not end the
world. Besides, it is far more likely to be a separate query than a
JOIN, and would only happen on views that required that data.

More to the point, what basis are you making this claim on? People
love to pipe up "JOINs are slow and evil", but have you actually
analysed the cost compared to monolithic tables?

I'm assuming that you will not have a monolithic table with 100s of columns because that would be silly but I can
see now that developers of third party apps might think that's the way to add user specific data. Like I said,
I think that having fields added to the user model by third party apps etc. is dumb but I never understood that anyone
thought that is the solution.

However, I still think JOINS across however many profiles tables being done automatically on every request is a bad idea.
That data should only be retrieved for views when it is needed. In the profiles solution you would *HAVE* to do joins which I think is bad.
In pluggable models the number of columns is user defined so you have control over that and can adjust based on the performance of your project.
 
> Assuming the user table and profile tables are small is awfully short
> sighted.

To be fair, I was slightly ambiguous with my use of the word 'small'. I said:

>> Queries against monolithic tables are much slower than a few queries on much
>> smaller tables.

Here 'small' means fewer columns, not less tuples.

However, assuming tables will be small is precisely what you have just
done - "we must not have JOINs, they are evil, but it doesn't matter
because the user table will only have the columns I desire". I agree
that it is a short sighted position, if you do not prevent the table
becoming monolithic ;)

Having lots of rows is more common than having lots and lots of columns. Breaking data into multiple tables
is good DB design and can generally be done by application designers. Dealing with tables with lots of rows
is much harder.

--
Ian

http://www.ianlewis.org/

Alex Ogier

da leggere,
10 apr 2012, 21:10:0010/04/12
a django-d...@googlegroups.com

Tai,

I think you are overestimating the importance of a "pluggable" user model. If 100 apps all try to add fields to the User model then of course bloat and performance issues and field name conflicts will be a problem. But I don't think that will happen. There are very good reasons for an app *not* to plug in: it's more work for everyone who uses your app and it means the app is incompatible with Django's default user. I trust app developers to recognize both of those facts.

This is orthogonal to the notion of swappable users. Django's default doesn't satisfy many projects' needs and we want to give them a way to implement their own user on a project-wide basis. Such users are "pluggable" merely because they are raw python, and raw python supports multiple inheritance and extensive monkey-patching. Setting a precedent here is fine IMO, and doesn't signal that every app should store all data related to the user on the user model.

Best,
Alex

On Apr 10, 2012 6:34 PM, "Tai Lee" <real....@mrmachine.net> wrote:

Tom Evans

da leggere,
13 apr 2012, 06:02:0813/04/12
a django-d...@googlegroups.com

Hi Alex, thanks for that. I think that a lot of this information
should end up in the revised docs, showing patterns on how to
(correctly) use the new features, when to add fields to user and when
not to, etc. and we should be fine.

I wasn't that interested in the magic proxying. Eventually, all magic
dies and has to be removed, so it is better to just be explicit about
what is happening.

Cheers

Tom

Hanne Moa

da leggere,
13 apr 2012, 15:39:3813/04/12
a django-d...@googlegroups.com
On 3 April 2012 04:56, Alex Ogier <alex....@gmail.com> wrote:
> I get that Django's core is very accustomed to the relational database mode
> of thinking: "If a User might own a Twitter handle, then let's create a
> table of twitter handles in the twitter-auth app, and foreign key back to
> the default User model". It's really not that bad to go the other way for a
> moment, and say "If a User might own a Twitter handle, then let's add a
> twitter_handle field to Users."

What if the User owns more than one Twitter handle? I use six
different email-addresses regularly and several openids. The auth
backends I write use Foreign Key and not OneToOneKey back to User for
a reason.

> The reason being that the latter is *so much
> more flexible*. You can simulate the first with the second, but not vice
> versa. Twitter-auth might not need its own table (in fact, it shouldn't need
> its own table). If you really wanted to, you could make one, and foreign-key
> *from* the user model which gives you everything the first solution has,
> with no need to created magical .prof1, .prof2 proxy attributes. You could
> even let users sign in with multiple handles with a many-to-many. Heck,
> maybe your blog posts have their own twitter credentials, I don't know.

This would *only* be flexible with a many-to-many. Then you wouldn't
have to ALTER TABLE to add an auth-backend.


HM

Bernardo Pires

da leggere,
22 mag 2012, 09:36:4422/05/12
a django-d...@googlegroups.com
Sorry for reviving this thread. Are the auth mixins fork from ogier going to be commited to the django master branch anytime soon? I'd like to "officially" use it. Is there any documentation available? Thanks in advance.

Am Dienstag, 3. April 2012 02:35:41 UTC+2 schrieb Jacob Kaplan-Moss:
Hi folks --

I've written up a proposal for how *I* would like to address refactoring auth.user: https://gist.github.com/2245327.

In essence, this does two things:

* Vastly "prunes" the required fields on auth.user. The only things left are an "identifier" (which could be username, email, url, uuid, whatever), and a password.
* Introduces a new "profile" system that provides a way to contribute extra related fields. Multiple profiles are supported, along with some syntactic sugar for dealing with multiple profiles in a reasonably reusable way.

And that's about it. I'm deliberately trying to find a middle ground between "do the minimum to allow people to move on" and "throw out and rewrite django.contrib.auth entirely". I'm not expecting everyone to be thrilled by this idea, but I'm hoping that this is "Good Enough" for almost everyone.

For more please see the document. Please do try to read the whole thing: I've had a few rounds of feedback incorporated already, and there's even an FAQ at the end.

I'm not using BDFL fiat here, at least not yet. This is a proposal, and I very much want to hear feedback, objections, and alternatives. I'm particularly interested in hearing from people who've got complicated auth needs and think this absolutely won't work for them. 

I *have* reviewed all the other proposals and I'm between -0 and -1 on all of them. This means that if you don't like my proposal, you'll probably have to come up with a complete *new* idea to have any chance of getting my vote.

Thanks!

Jacob
Rispondi a tutti
Rispondi all'autore
Inoltra
0 nuovi messaggi