--
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'm not in favour of pluggable user models, as for me, they solve the
wrong problem. A pluggable user model has to be set up by the project
developer, whilst the attributes an app may need are specified solely
by the app developer.
If a project developer decides to add a 3rd party app to his project,
where do the user preferences for that app live? Does the user model
automatically get expanded with the extra fields required by that app?
It all seems icky to me.
To my mind, User + app specific user profiles are the correct
approach, plus fixing the current minor issues with d.c.a., and
providing tools and documentation to allow users to manage that
change.
Put another way, what does a pluggable user model get us? What is the
big selling point, apart from being able to specify arbitrary columns
to appear in auth_user rather than myapp_userprofile.
Cheers
Tom
I'm not in favour of pluggable user models, as for me, they solve thewrong problem. A pluggable user model has to be set up by the projectdeveloper, whilst the attributes an app may need are specified solelyby the app developer.If a project developer decides to add a 3rd party app to his project,where do the user preferences for that app live? Does the user modelautomatically get expanded with the extra fields required by that app?It all seems icky to me.To my mind, User + app specific user profiles are the correctapproach, plus fixing the current minor issues with d.c.a., andproviding tools and documentation to allow users to manage thatchange.Put another way, what does a pluggable user model get us? What is thebig selling point, apart from being able to specify arbitrary columnsto appear in auth_user rather than myapp_userprofile.
CheersTom
I personally find the User/Profile mechanism in Django to be quite awkward, to be honest. It's certainly not the end of the world, but it'd be nice if it was...less contrived feeling.
I have an interesting suggestion/compromise: What about a mechanism that allows the app developer to *add* fields to the User model, but not change (or remove) existing keys? From my vantage point, this would have nearly all of the benefits of a pluggable user model with almost none of the drawbacks, and it would feel much more straightforward than the current mechanism. There may be some reason that I'm missing as to why this is foolish, though.
Here's my hit list of perceived benefits:
1. It regains the efficiency of a single table (which is minor, to be sure, but since Jacob already brought it up...)
2. It allows app developers who just need a little bit more than what the User model does to add their custom fields seamlessly. This would mean, for instance,
a. That the admin would just magically have the new fields in the User form, instead of having to either plug in a custom form, set up a separate ModelAdmin for your profile, or whatever else. For registration, we could offer two stock forms (one minimalist one, and one complete one that just blindly uses every field we don't know we want to exclude (e.g. is_staff)). If neither form works for the end developer, then they write and use their own.
b. That the end developers don't have to write repetitive glue code on every one of their projects to make their profile models work. (Why do I have to write a signal to auto-save a profile object on User object save every time, for instance?)
3. We don't have myapp_userprofile. This is kind of a big deal to me, because in most of my projects there is no "clear" / "correct" place for this model to live -- there's no single app that handles users, because that's what django.contrib.auth is supposed to do. Getting these arbitrary columns into auth_user is a nice categorization win.
If I understand this correctly, we avoid the following drawbacks of a fully pluggable system:
1. Apps can still be written to expect certain fields on User to exist, because the customization only allows the addition (not alteration) of fields.
2. We aren't expecting nearly every Django installation to write their own User model just to make a small addition to what stock Django offers; if you want just an extra field for birthdate, you don't write an entire custom user model; you just add the field. Since "I just want a few extra fields" is (other than the email address / username snafu) probably the most common need for auth, making this easy is a big win.
What this does *not* solve is a situation where there's a field on User that's somehow wrong, such as the 30/75 character limitations on usernames and email addresses. Presumably this is done as part of a completionist revamp where we also fix those problems.
Best Regards,
Luke
P. S. I've lurked on the Django dev list for a long time, but never gotten my feet wet. That said, the improvements to auth being discussed are something I really want, and I want them enough to help out non-trivially if afforded the opportunity. I was planning on emailing Carl and Russell to pick their brains later, but maybe it's worth a public statement.
Disclaimer: I was up really, really early this morning, so please bear with me if I sound somewhat incoherent...On March 16, 2012, at 12:29 , Tom Evans wrote:On Fri, Mar 16, 2012 at 4:53 PM, Jacob Kaplan-Moss <ja...@jacobian.org> wrote:Hi folks --[…]I'm not in favour of pluggable user models, as for me, they solve thewrong problem. A pluggable user model has to be set up by the projectdeveloper, whilst the attributes an app may need are specified solelyby the app developer.If a project developer decides to add a 3rd party app to his project,where do the user preferences for that app live? Does the user modelautomatically get expanded with the extra fields required by that app?It all seems icky to me.To my mind, User + app specific user profiles are the correctapproach, plus fixing the current minor issues with d.c.a., andproviding tools and documentation to allow users to manage thatchange.Put another way, what does a pluggable user model get us? What is thebig selling point, apart from being able to specify arbitrary columnsto appear in auth_user rather than myapp_userprofile.I personally find the User/Profile mechanism in Django to be quite awkward, to be honest. It's certainly not the end of the world, but it'd be nice if it was...less contrived feeling.I have an interesting suggestion/compromise: What about a mechanism that allows the app developer to *add* fields to the User model, but not change (or remove) existing keys? From my vantage point, this would have nearly all of the benefits of a pluggable user model with almost none of the drawbacks, and it would feel much more straightforward than the current mechanism. There may be some reason that I'm missing as to why this is foolish, though.Here's my hit list of perceived benefits:1. It regains the efficiency of a single table (which is minor, to be sure, but since Jacob already brought it up...)
2. It allows app developers who just need a little bit more than what the User model does to add their custom fields seamlessly. This would mean, for instance,
a. That the admin would just magically have the new fields in the User form, instead of having to either plug in a custom form, set up a separate ModelAdmin for your profile, or whatever else. For registration, we could offer two stock forms (one minimalist one, and one complete one that just blindly uses every field we don't know we want to exclude (e.g. is_staff)). If neither form works for the end developer, then they write and use their own.b. That the end developers don't have to write repetitive glue code on every one of their projects to make their profile models work. (Why do I have to write a signal to auto-save a profile object on User object save every time, for instance?)3. We don't have myapp_userprofile. This is kind of a big deal to me, because in most of my projects there is no "clear" / "correct" place for this model to live -- there's no single app that handles users, because that's what django.contrib.auth is supposed to do. Getting these arbitrary columns into auth_user is a nice categorization win.If I understand this correctly, we avoid the following drawbacks of a fully pluggable system:1. Apps can still be written to expect certain fields on User to exist, because the customization only allows the addition (not alteration) of fields.
2. We aren't expecting nearly every Django installation to write their own User model just to make a small addition to what stock Django offers; if you want just an extra field for birthdate, you don't write an entire custom user model; you just add the field. Since "I just want a few extra fields" is (other than the email address / username snafu) probably the most common need for auth, making this easy is a big win.What this does *not* solve is a situation where there's a field on User that's somehow wrong, such as the 30/75 character limitations on usernames and email addresses. Presumably this is done as part of a completionist revamp where we also fix those problems.
Best Regards,LukeP. S. I've lurked on the Django dev list for a long time, but never gotten my feet wet. That said, the improvements to auth being discussed are something I really want, and I want them enough to help out non-trivially if afforded the opportunity. I was planning on emailing Carl and Russell to pick their brains later, but maybe it's worth a public statement.
sorry, if this was said before, I haven't read the latest user discussions.
I'm in favor of enhancing the auth app step by step, as everything else
seems unlikely (haven't happend for a long time, why should it now).
What I dislike about the current auth app in general is that it solves
differnt things. You either have to take it all or do everything
yourself. So perhaps a first step towards a new and shiny auth
implementation might be to split things up? What do you think?
Currently auth consists of multiple things:
* authentication
* authorization / permissions
* Users
* Groups
In any case, the current auth system isn't all bad. I like many things
and it works well for most cases. The email-login issue may be solves
with randomly generated usernames and a authentication backend that
matches by email-field instead. This all isn't perfect, but small steps
may be enough to get to a nearly perfect solution here.
David
David
I'm also interested in working on an enhanced auth.user as a GSoC project
and I'm currently working on my proposal. My proposal is largely based on
pluggable auth models.
What I'm seeing is that most people are generally negative to the idea,
including core developers and django-developers' regulars. I have also
read through the comments of ticket #3011 and the linked Google
spreadsheet[1] where the idea was eventually rejected for 1.1. Moreover,
Django's GSoC 2011 page (but not the one for 2012) specifically mentions
that the idea has been rejected.
That said, I believe that my proposal, when completed, will address most
of the issues people are raising against the idea of pluggable user models.
My question is whether I should continue writing/developing my proposal or
is this something that has no chance of being accepted?
Thanks.
Stratos Moros
1: http://spreadsheets.google.com/ccc?key=pSqnCvef6OXmGWQ9qbEVMeA
On Friday, March 16, 2012 at 3:59 PM, David Danier wrote:
...
Currently auth consists of multiple things:* authentication* authorization / permissions* Users* Groups
Hello,I would like to share some early stage thoughts on this matter.On Fri, Mar 16, 2012 at 5:01 PM, Donald Stufft <donald...@gmail.com> wrote:On Friday, March 16, 2012 at 3:59 PM, David Danier wrote:...Currently auth consists of multiple things:* authentication* authorization / permissions* Users* GroupsMaybe the problem is that we think an User as a person. The User model could be more like an Account, having only attributes related to authentication.In that case, even the email address could be placed somewhere else. I understand that the email address is important, but for registration purposes, not for authentication. It seems to me that what really describes an User is in fact the User Profile (as metadata about an account).
If we could separate the user creation process from the User model and put it as part of a registration entity or something, a project could extend/override it to set the account content and create the auxiliary models properly. This would allow the use of an username, or an email or any kind of unique identifier. We still would need to fix the column length, but this would help to keep the authentication process consistent.This would have impact on the admin. But maybe the User attributes needed by the admin should be provided there, as a model with FK to the Account.All the best,--
HB
Ok - I've been keeping quiet on this; partially due to the same tone issues that you've described, but also because I don't have a huge amount of spare time at the moment, and I don't want to be the person who makes a bunch of promises (expressed or implied) to work on something and then can't deliver on those promises.
My biggest concern is that the middle-ground proposals that are on the table are implementing a workaround for a problem when the starting point of a long-term solution won't require that much more effort.
IMHO, rather than implement a bunch of settings to introduce an auth.User-specific solution, we need to do two things:
* Merge the app-refactor from GSoC 2010.
This has a whole bunch of long-needed awaited benefits -- reliable hooks for app startup, configurable app labels, predictable module loading, amongst others -- but the one that matters for the purposes of auth.User is that it allows Apps to be treated as items that need to be configured as a runtime activity. In this case, we need to be able to specify, at a project level, which model is your "User" model in the auth app.
* Add the concept of a LazyForeignKey.
LazyForeignKey is a normal foreign key, with all the usual foreign key behaviors; the only difference is that the model it links to isn't specified in the model -- it's a configuration item drawn from an application configuration. So, ForeignKey('auth.User') creates a foreign key to django.contrib.auth.User; LazyForeignKey('auth.User') asks the auth app for the model that is being used as the 'User' model, and creates a foreign key to that. This isn't a huge body of work -- it's just a matter of slotting into the existing model reference resolution code (which is something that the app refactor cleans up).
What are the benefits of this approach?
* It solves the general problem, rather than just focussing on auth.User.
The broad idea of defining behavior against an interface, rather than a concrete model, is hardly new; auth.User is just the most obvious manifestation in Django's problem space. It doesn't take much work to find other examples: contrib.comments has an analogous "choose the comment model" problem (solved in it's own bespoke way). It wouldn't take much of a survey to find a whole lot of similar examples.
If we just add settings to make auth.User configurable, we don't address the analgous problems of any other app.
* It's completely backwards compatible.
If you've got an existing app with normal ForeignKeys to auth.User, the app will continue to work, without any migrations, as long as the rest of your project uses auth.User. It will also co-exist with any configurable app that is configured to use auth.User as the user model. It will only fall down if you have a configurable app that uses a different User model (i.e., you can't use a new feature without ensuring all the parts you need support the new feature).
* It's got a relatively simple forward migration path for app authors
For most of the use cases being discussed in this thread (e.g., make email max_length=254), just replace ForeignKey(User) in your app with LazyForeignKey('admin.User'), and it should work without any problems.
More specifically, you just need to be aware of the contract that you expect a User model to honor. We can help out here by clearly documenting the "admin" User contract (and doing a light refactor of admin as required to adhere to use that contract -- e.g., replacing uses of user.username with a "get_identifer()" type method). This would have the effect of setting a defacto standard for what you can expect to find on a User model.
* It solves the immediate problem ...
As I see it, the immediate problem is that developers want to be able to modify the base requirements of auth.User. There may well be people who want to completely change contrib.auth, but for the moment, the 90% case can be solved by modifying max_length or setting unique=True on the email field, and/or removing the username field. The rest of auth.User is fine, at least for now.
* ... without making a rod for our back long term ...
Once we have a configurable Auth app, we can work out the details of what else needs to be configured, and how, in order to satisfy other use cases that are presented. However, we'll have tackled the hard part -- getting a framework in place for adding configuration points -- as part of the initial effort.
* ... or introducing a bunch of settings.
'nuff said. In fact, it would potentially allow us to *deprecate* a bunch of settings (e.g., REDIRECT_FIELD_NAME, LOGIN_URL) and make them application settings.
Yours,
Russ Magee %-)
The thing is, there's two separate problems here.
Problem 1 is the problem of identity. auth.User currently enforces a specific set of restrictions on the username, email and "name" fields -- the fields dealing with identity -- which are critical to the operation of a User object, but don't have a single, canonical set of correct settings. Some people want a unique email field with max_length=254, some want a single "full name" field, rather than "first name"/"last name", and so on. There's currently no way for developers to support these decisions without forking and/or monkeypatching contrib.auth.models.
Problem 2 is that people want to put their profile information on their User model. I completely agree that this is the wrong solution, and app-specific profile models are the way to go here.
The reason to look at pluggable User models isn't problem 2 -- it's problem 1. Problem 2 already has a solution, and a good one at that IMHO. However, there isn't currently a good solution for problem 1, which is the root cause of most of the frustration in this thread, AFAICT.
Yours,
Russ Magee %-)
I wanted to support the idea of the pluggable User model, but Russell just sent an excellent argumentation, saving me the effort :) I agree very much with his proposal.
Subclassing, or writing an API-compatible class (duck typing) is the canonical way to alter a built-in behavior in Python.
Besides, I find project-wide "user profiles" impractical for most purposes. I prefer subclassing auth.User even if I just need a few extra fields, and add enough glue (middleware, auth backend, login and logout views) to hold the pieces together.
So, what I always wanted is a configurable User model.
This is the second most messy workaround in my current project (after datetime handling, which was the motivation behind my work on time zone support in 1.4), and I'm willing to work on improving the situation.
As far as I can tell, app-refactor isn't very far from completion, and there's no opposition to the concept of LazyForeignKeys, only a simplistic implementation was rejected (the archives on this topic are sparse and sometimes confusing).
Best regards,
--
Aymeric.
In my latest project there's a situation where neither is useful.
We have a Person model (name, address, phone, FK company etc.), and
that person may or may not (yet!) have a User account.
Person gets a nullable FK from User and we have to listen to
post_save() on both of them to synchronize first_name/last_name/email.
Would be awesome if such a use case was covered.
Cheers,
Danny
Eric Florenzano and I actually had a discussion about this at PyCon.
My company does Django development and simply doesn't use the Django
auth app because it tries to do authentication and authorization in
one app and the User models are just to inflexible. Many projects
didn't need or want usernames, email etc. and many required changing
the fields on the model. Using the get_profile() method in every view
and in every template was also a huge pain in the ass.
I have a concrete proposal and implementation that I wrote for our use
that solves a lot of the issues that have come up here. I have been
brushing up the api and documentation recently to release it but I'd
like to speed things up and release it publicly. The app is built on
some basic tenets in order of importance:
1. Django shouldn't decide what fields go on the user model. The app
provides an abstract base class which developers subclass to add the
appropriate fields they need.
2. Django shouldn't decide the type of the primary key. The app only
relies on the fact that the object has a pk. The id field can be named
anything you wish and can be any type (integer, char, uuid, etc.).
3. Third party apps don't rely on the user having any fields but
rather the base user class defines methods that are implemented by
subclasses. Methods like get_display_name() which provides a way for
third party apps to get something to display.
4. Rather than provide mixins or something, we should have conventions
for the field names like 'email' and third party apps should check if
the user has one using duck typing e.g. hasattr(user, 'email'). An
alternative could be to provide some kind of API for commonly used
actions like emailing users.
5. Basic username (or email)/password authentication can be provided.
The app has a base user class from which a basic abstract user with
username/password is defined. This can implement setting passwords
properly and provide forms etc.
6. Multiple user models can be defined (Like say for normal users and
affiliate users or admin users). If one wants to create a project
currently with a separate user model, none of the machinery in the
auth app can be used. With the above approach multiple user models can
be defined and used without any problems. It doesn't make much sense
to only allow one user model and only that model can use the auth API
and machinery. A default user model can be defined and imported via
the models module. Admin users can also be defined completely
separately from normal users.
You create users by creating your own app in your project and creating
a User there:
account/models.py
from django.db import models
from newauth.models import UserBase
class User(BaseUser):
full_name = models.CharField(u"Full Name", max_length=255)
email = models.EmailField('Email Address')
profile = models.TextField('Profile Bio', blank=True, null=True)
avatar = models.ImageField('Avatar', upload_to='profileimg/',
blank=True, null=True)
def get_display_name(self):
return self.full_name
class Meta:
db_table = 'my_user_table'
verbose_name = u"Djangonaut"
verbose_name_plural = u"Djangonaut"
There are even docs and tests.
Check the source out here: https://bitbucket.org/IanLewis/django-newauth/
See the docs here: http://ianlewis.bitbucket.org/django-newauth/
Having used this at my company for over a year, I can say it's
extremely nice and has worked well for us. I hope that the
implementation and docs can serve as a reference for any improvements
to Django auth.
Some other miscellaneous thoughts below:
On 17/03/2012, at 12:53 AM, Jacob Kaplan-Moss wrote:
> 2. Complete improvement: recognize that the auth app is fundamentally flawed, and mercilessly refactor/replace/rewrite it, all in one go. The hypothetical results here would be better -- a modern auth system unencumbered by the decisions we made in 2005 -- but this would take far longer, and would block on things like the app refactor and schema migrations.
I'm in favor of this kind of solution personally but I think it will
really throw other projects for a loop. Especially projects like Pinax
which rely heavily on the auth.User models.
>> There's also a middle-ground proposal from Clay: make the auth app swappable, thus making it possible for *users* to replace the auth app while leaving time for us to make either incremental or complete change, as we see fit.
I'm not really in favor of this for the reasons Jacob mentioned.
On Sat, Mar 17, 2012 at 2:59 PM, Russell Keith-Magee
<rus...@keith-magee.com> wrote:
> * Add the concept of a LazyForeignKey.
>
> LazyForeignKey is a normal foreign key, with all the usual foreign key behaviors; the only difference is that the model it links to isn't specified in the model -- it's a configuration item drawn from an application configuration. So, ForeignKey('auth.User') creates a foreign key to django.contrib.auth.User; LazyForeignKey('auth.User') asks the auth app for the model that is being used as the 'User' model, and creates a foreign key to that. This isn't a huge body of work -- it's just a matter of slotting into the existing model reference resolution code (which is something that the app refactor cleans up).
I agree this would help a lot but I think if the model could be
imported from a specific module (i.e. so that third party apps could
import it) this wouldn't be so much of a problem. That said, I want
this for other reasons ;)
> What are the benefits of this approach?
>
> * It solves the general problem, rather than just focussing on auth.User.
>
> The broad idea of defining behavior against an interface, rather than a concrete model, is hardly new; auth.User is just the most obvious manifestation in Django's problem space. It doesn't take much work to find other examples: contrib.comments has an analogous "choose the comment model" problem (solved in it's own bespoke way). It wouldn't take much of a survey to find a whole lot of similar examples.
I think the comments app doesn't do a terribly good job at solving the
problem and won't make a good reference.
> If we just add settings to make auth.User configurable, we don't address the analgous problems of any other app.
>
> * It's completely backwards compatible.
>
> If you've got an existing app with normal ForeignKeys to auth.User, the app will continue to work, without any migrations, as long as the rest of your project uses auth.User. It will also co-exist with any configurable app that is configured to use auth.User as the user model. It will only fall down if you have a configurable app that uses a different User model (i.e., you can't use a new feature without ensuring all the parts you need support the new feature).
>
> * It's got a relatively simple forward migration path for app authors
>
> For most of the use cases being discussed in this thread (e.g., make email max_length=254), just replace ForeignKey(User) in your app with LazyForeignKey('admin.User'), and it should work without any problems.
>
> More specifically, you just need to be aware of the contract that you expect a User model to honor. We can help out here by clearly documenting the "admin" User contract (and doing a light refactor of admin as required to adhere to use that contract -- e.g., replacing uses of user.username with a "get_identifer()" type method). This would have the effect of setting a defacto standard for what you can expect to find on a User model.
This is going to be the biggest problem with my solution. There would
probably have to be some kind of compatibility layer added to make
existing apps work or to provide a simpler migration path.
> * It solves the immediate problem ...
>
> As I see it, the immediate problem is that developers want to be able to modify the base requirements of auth.User. There may well be people who want to completely change contrib.auth, but for the moment, the 90% case can be solved by modifying max_length or setting unique=True on the email field, and/or removing the username field. The rest of auth.User is fine, at least for now.
I agree this is the most immediate problem. If you could do this it
would be ok though I have other issues with auth that prevent me from
using it so even if I could modify the fields on auth.User I still
won't use it.
> * ... without making a rod for our back long term ...
>
> Once we have a configurable Auth app, we can work out the details of what else needs to be configured, and how, in order to satisfy other use cases that are presented. However, we'll have tackled the hard part -- getting a framework in place for adding configuration points -- as part of the initial effort.
> The reason to look at pluggable User models isn't problem 2 -- it's problem 1. Problem 2 already has a solution, and a good one at that IMHO. However, there isn't currently a good solution for problem 1, which is the root cause of most of the frustration in this thread, AFAICT.
There isn't much you need on a user object besides a primary key and
maybe a way to authenticate it. Everything else is profile so things
like the name, email, etc. are all profile. I just don't really see
the benefit of breaking all this up and requiring you to do multiple
lookups in every view or make a middleware that goes and gets the
profile object for you. It's terribly inconvenient. On top of that the
difference between a user and a profile isn't really all that clear
in a lot of apps. I think if you need or want this kind of topology
you can create it yourself in your project.
--
Ian
On Friday, March 16, 2012 at 2:08 PM, Luke Sneeringer wrote:Here's my hit list of perceived benefits:1. It regains the efficiency of a single table (which is minor, to be sure, but since Jacob already brought it up...)This issue isn't as minor as one might think I don't believe. I've recently started using the fetch_related thing (which would be the only way to prevent N queries when trying to select a bunch of users + their profiles) and on a table with 20k entries (this isn't User related) where the entries are fairly large the processing time to fetch them all was very significant. This effect would increase with Tom's per app profile issue.
* ... without making a rod for our back long term ...Once we have a configurable Auth app, we can work out the details of what else needs to be configured, and how, in order to satisfy other use cases that are presented. However, we'll have tackled the hard part -- getting a framework in place for adding configuration points -- as part of the initial effort.* ... or introducing a bunch of settings.'nuff said. In fact, it would potentially allow us to *deprecate* a bunch of settings (e.g., REDIRECT_FIELD_NAME, LOGIN_URL) and make them application settings.Yours,Russ Magee %-)
> Hi,
>
> Eric Florenzano and I actually had a discussion about this at PyCon.
> My company does Django development and simply doesn't use the Django
> auth app because it tries to do authentication and authorization in
> one app and the User models are just to inflexible. Many projects
> didn't need or want usernames, email etc. and many required changing
> the fields on the model. Using the get_profile() method in every view
> and in every template was also a huge pain in the ass.
>
> I have a concrete proposal and implementation that I wrote for our use
> that solves a lot of the issues that have come up here. I have been
> brushing up the api and documentation recently to release it but I'd
> like to speed things up and release it publicly. The app is built on
> some basic tenets in order of importance:
>
> 1. Django shouldn't decide what fields go on the user model. The app
> provides an abstract base class which developers subclass to add the
> appropriate fields they need.
+1
> 2. Django shouldn't decide the type of the primary key. The app only
> relies on the fact that the object has a pk. The id field can be named
> anything you wish and can be any type (integer, char, uuid, etc.).
+1
> 3. Third party apps don't rely on the user having any fields but
> rather the base user class defines methods that are implemented by
> subclasses. Methods like get_display_name() which provides a way for
> third party apps to get something to display.
> 4. Rather than provide mixins or something, we should have conventions
> for the field names like 'email' and third party apps should check if
> the user has one using duck typing e.g. hasattr(user, 'email'). An
> alternative could be to provide some kind of API for commonly used
> actions like emailing users.
This is essentially all I was proposing when I spoke of an "admin User contract"; that we define some basic "identity" functions that every User object is expected to provide -- short name, long name, and so on.
The admin case is a little more complicated because there is also a required API for permissions and groups, but to my mind, these are different contracts, and should be documented as such.
> 5. Basic username (or email)/password authentication can be provided.
> The app has a base user class from which a basic abstract user with
> username/password is defined. This can implement setting passwords
> properly and provide forms etc.
> 6. Multiple user models can be defined (Like say for normal users and
> affiliate users or admin users). If one wants to create a project
> currently with a separate user model, none of the machinery in the
> auth app can be used.
Sure you can -- you have a base User, and then subclasses to get AdminUser and NormalUser -- both of which are effectively just another type of UserProfile.
> You create users by creating your own app in your project and creating
> a User there:
>
> account/models.py
>
> from django.db import models
>
> from newauth.models import UserBase
>
> class User(BaseUser):
> full_name = models.CharField(u"Full Name", max_length=255)
> email = models.EmailField('Email Address')
> profile = models.TextField('Profile Bio', blank=True, null=True)
> avatar = models.ImageField('Avatar', upload_to='profileimg/',
> blank=True, null=True)
>
> def get_display_name(self):
> return self.full_name
>
> class Meta:
> db_table = 'my_user_table'
> verbose_name = u"Djangonaut"
> verbose_name_plural = u"Djangonaut"
>
> There are even docs and tests.
How does this address the issue of reusable apps referencing User? Let's say I write a comments app, and want an Author field. I need a ForeignKey to "User". But I can't have a foreign key to BaseUser, because it's an abstract class. How do I define my Comment model in such a way that it can reference a generic "User"?
It seems to me that the solution you're proposing requires the LazyFK and app-refactor infrastructure I've described in order to be useful in the general case (not that I'm complaining, mind -- just pointing out that our two proposals are complementary :-).
>> What are the benefits of this approach?
>>
>> * It solves the general problem, rather than just focussing on auth.User.
>>
>> The broad idea of defining behavior against an interface, rather than a concrete model, is hardly new; auth.User is just the most obvious manifestation in Django's problem space. It doesn't take much work to find other examples: contrib.comments has an analogous "choose the comment model" problem (solved in it's own bespoke way). It wouldn't take much of a survey to find a whole lot of similar examples.
>
> I think the comments app doesn't do a terribly good job at solving the
> problem and won't make a good reference.
I'm not arguing that contrib.comments is a good reference -- quite the opposite. I'm arguing that it has the same root problem as auth.User, and it has implemented a barely adequate "pluggable" solution. To my reading, the 'minimal' solution on the table is really just repeating the same pattern in a new implementation.
>> If we just add settings to make auth.User configurable, we don't address the analgous problems of any other app.
>>
>> * It's completely backwards compatible.
>>
>> If you've got an existing app with normal ForeignKeys to auth.User, the app will continue to work, without any migrations, as long as the rest of your project uses auth.User. It will also co-exist with any configurable app that is configured to use auth.User as the user model. It will only fall down if you have a configurable app that uses a different User model (i.e., you can't use a new feature without ensuring all the parts you need support the new feature).
>>
>> * It's got a relatively simple forward migration path for app authors
>>
>> For most of the use cases being discussed in this thread (e.g., make email max_length=254), just replace ForeignKey(User) in your app with LazyForeignKey('admin.User'), and it should work without any problems.
>>
>> More specifically, you just need to be aware of the contract that you expect a User model to honor. We can help out here by clearly documenting the "admin" User contract (and doing a light refactor of admin as required to adhere to use that contract -- e.g., replacing uses of user.username with a "get_identifer()" type method). This would have the effect of setting a defacto standard for what you can expect to find on a User model.
>
> This is going to be the biggest problem with my solution. There would
> probably have to be some kind of compatibility layer added to make
> existing apps work or to provide a simpler migration path.
Isn't the compatibility layer just an implementation of the existing auth.User class that extends from BaseUser? We're going to have to ship this user class anyway, so that everything works out of the box; then if anyone wants to define their own User class, they can.
>> * It solves the immediate problem ...
>>
>> As I see it, the immediate problem is that developers want to be able to modify the base requirements of auth.User. There may well be people who want to completely change contrib.auth, but for the moment, the 90% case can be solved by modifying max_length or setting unique=True on the email field, and/or removing the username field. The rest of auth.User is fine, at least for now.
>
> I agree this is the most immediate problem. If you could do this it
> would be ok though I have other issues with auth that prevent me from
> using it so even if I could modify the fields on auth.User I still
> won't use it.
I'm not completely convinced that your proposal isn't just the "long term refactor" of auth that I referred to. I've only had a quick look at your code, but it seems to share a lot of similarities with oldauth. Yes, there have been modifications to remove dependencies on certain attributes of User, but from my quick check, I didn't see anything that we couldn't achieve through a process of modification of the existing code (if we didn't have an existing pret-a-porter implementation like yours)
>> * ... without making a rod for our back long term ...
>>
>> Once we have a configurable Auth app, we can work out the details of what else needs to be configured, and how, in order to satisfy other use cases that are presented. However, we'll have tackled the hard part -- getting a framework in place for adding configuration points -- as part of the initial effort.
>
>> The reason to look at pluggable User models isn't problem 2 -- it's problem 1. Problem 2 already has a solution, and a good one at that IMHO. However, there isn't currently a good solution for problem 1, which is the root cause of most of the frustration in this thread, AFAICT.
>
> There isn't much you need on a user object besides a primary key and
> maybe a way to authenticate it. Everything else is profile so things
> like the name, email, etc. are all profile. I just don't really see
> the benefit of breaking all this up and requiring you to do multiple
> lookups in every view or make a middleware that goes and gets the
> profile object for you. It's terribly inconvenient. On top of that the
> difference between a user and a profile isn't really all that clear
> in a lot of apps. I think if you need or want this kind of topology
> you can create it yourself in your project.
Yes, all that is strictly needed is a PK and a way to authenticate -- but in practice, a user object isn't much use unless you can say "Hello <user>", so I don't see why basic identity mechanisms shouldn't be part of the basic User contract (not necessarily defined at a data level, just at a data access level).
Personally, I don't have a problem with UserProfile as a pattern, especially when it comes to per-app settings. However, the good news is that both approaches are possible once the User model is configurable. We can describe the approach of a generic User with UserProfile objects containing app settings; and also document the fact that if you have performance (or taste/convenience) concerns about the joins to a UserProfile object, you can define a per-project User object that incorporates all the profile data you want.
Yours,
Russ Magee %-)
> On Saturday, March 17, 2012 at 1:59 AM, Russell Keith-Magee wrote:
>>
>> * It solves the immediate problem ...
>>
>> As I see it, the immediate problem is that developers want to be able to modify the base requirements of auth.User. There may well be people who want to completely change contrib.auth, but for the moment, the 90% case can be solved by modifying max_length or setting unique=True on the email field, and/or removing the username field. The rest of auth.User is fine, at least for now.
> It doesn't really solve the immediate problem though. It does technically but it also requires every single app that uses auth.User to update itself
> before you can use it which makes it less useful.
To my mind, this isn't any different to the situation with any other major feature added in a Django release:
a) Existing apps continued to work as is.
b) Under certain conditions, existing apps will be able to use the new features transparently
c) To be completely compatible, some modifications to the existing app are required
Yes, this means that some apps will need to be labelled as "has been updated for the 1.5 User model" -- but that's not without precedent.
The only way I can see around this problem is to come up with a way for ForeignKey(User) to transparently become an effective LazyForeignKey('auth.User'). If we could manage this, then any existing ForeignKey(User) would adapt to the new class, so the set of apps in class (c) would be reduced to those that use features of User that aren't part of the new User contract.
Personally, I like the idea of having an explicit "Lazy" reference; but we might be able to address this in other ways. For example, if we mark a model class as "swappable", references to that class would be resolved to the class named in the app configuration. This might also provide the hook to prevent the auth_user table from being created by syncdb (so we don't get empty auth_user tables being created)
Yours,
Russ Magee %-)
On Sun, Mar 18, 2012 at 9:41 AM, Russell Keith-Magee
<rus...@keith-magee.com> wrote:
>> 1. Django shouldn't decide what fields go on the user model. The app
>> provides an abstract base class which developers subclass to add the
>> appropriate fields they need.
>
> +1
THX
>> 2. Django shouldn't decide the type of the primary key. The app only
>> relies on the fact that the object has a pk. The id field can be named
>> anything you wish and can be any type (integer, char, uuid, etc.).
>
> +1
THX again
>> 3. Third party apps don't rely on the user having any fields but
>> rather the base user class defines methods that are implemented by
>> subclasses. Methods like get_display_name() which provides a way for
>> third party apps to get something to display.
>> 4. Rather than provide mixins or something, we should have conventions
>> for the field names like 'email' and third party apps should check if
>> the user has one using duck typing e.g. hasattr(user, 'email'). An
>> alternative could be to provide some kind of API for commonly used
>> actions like emailing users.
>
> This is essentially all I was proposing when I spoke of an "admin User contract"; that we define some basic "identity" functions that every User object is expected to provide -- short name, long name, and so on.
>
> The admin case is a little more complicated because there is also a required API for permissions and groups, but to my mind, these are different contracts, and should be documented as such.
My solution is simply authentication, authorization would need to be
added on or in a separate app built on top of newauth.
>> 5. Basic username (or email)/password authentication can be provided.
>> The app has a base user class from which a basic abstract user with
>> username/password is defined. This can implement setting passwords
>> properly and provide forms etc.
>> 6. Multiple user models can be defined (Like say for normal users and
>> affiliate users or admin users). If one wants to create a project
>> currently with a separate user model, none of the machinery in the
>> auth app can be used.
>
> Sure you can -- you have a base User, and then subclasses to get AdminUser and NormalUser -- both of which are effectively just another type of UserProfile.
I meant one that was a completely separate concrete base model. The
current auth forces you to take along with you all the fields on the
User model.
>> You create users by creating your own app in your project and creating
>> a User there:
>>
>> account/models.py
>>
>> from django.db import models
>>
>> from newauth.models import UserBase
>>
>> class User(BaseUser):
>> full_name = models.CharField(u"Full Name", max_length=255)
>> email = models.EmailField('Email Address')
>> profile = models.TextField('Profile Bio', blank=True, null=True)
>> avatar = models.ImageField('Avatar', upload_to='profileimg/',
>> blank=True, null=True)
>>
>> def get_display_name(self):
>> return self.full_name
>>
>> class Meta:
>> db_table = 'my_user_table'
>> verbose_name = u"Djangonaut"
>> verbose_name_plural = u"Djangonaut"
>>
>> There are even docs and tests.
>
> How does this address the issue of reusable apps referencing User? Let's say I write a comments app, and want an Author field. I need a ForeignKey to "User". But I can't have a foreign key to BaseUser, because it's an abstract class. How do I define my Comment model in such a way that it can reference a generic "User"?
>
> It seems to me that the solution you're proposing requires the LazyFK and app-refactor infrastructure I've described in order to be useful in the general case (not that I'm complaining, mind -- just pointing out that our two proposals are complementary :-).
This is a bad example for showing how that works. I just wanted to
illustrate how you would make your own User model. In the case where
you want a foreign key to User you can import the default user model
from newauth.models as User much like you do with the current django
auth app.
See: http://ianlewis.bitbucket.org/django-newauth/third_party.html
>> This is going to be the biggest problem with my solution. There would
>> probably have to be some kind of compatibility layer added to make
>> existing apps work or to provide a simpler migration path.
>
> Isn't the compatibility layer just an implementation of the existing auth.User class that extends from BaseUser? We're going to have to ship this user class anyway, so that everything works out of the box; then if anyone wants to define their own User class, they can.
Perhaps. I think in reality it will be a bit more complicated though I
haven't really thought about it. I didn't really consider
authorization or backwards compatibility as a goal of the project when
first writing it.
>>> * It solves the immediate problem ...
>>>
>>> As I see it, the immediate problem is that developers want to be able to modify the base requirements of auth.User. There may well be people who want to completely change contrib.auth, but for the moment, the 90% case can be solved by modifying max_length or setting unique=True on the email field, and/or removing the username field. The rest of auth.User is fine, at least for now.
>>
>> I agree this is the most immediate problem. If you could do this it
>> would be ok though I have other issues with auth that prevent me from
>> using it so even if I could modify the fields on auth.User I still
>> won't use it.
>
> I'm not completely convinced that your proposal isn't just the "long term refactor" of auth that I referred to. I've only had a quick look at your code, but it seems to share a lot of similarities with oldauth. Yes, there have been modifications to remove dependencies on certain attributes of User, but from my quick check, I didn't see anything that we couldn't achieve through a process of modification of the existing code (if we didn't have an existing pret-a-porter implementation like yours)
That's perhaps true. I suppose the primary key thing and being able to
separate admin and regular users were the things I personally wanted
the most.
>> There isn't much you need on a user object besides a primary key and
>> maybe a way to authenticate it. Everything else is profile so things
>> like the name, email, etc. are all profile. I just don't really see
>> the benefit of breaking all this up and requiring you to do multiple
>> lookups in every view or make a middleware that goes and gets the
>> profile object for you. It's terribly inconvenient. On top of that the
>> difference between a user and a profile isn't really all that clear
>> in a lot of apps. I think if you need or want this kind of topology
>> you can create it yourself in your project.
>
> Yes, all that is strictly needed is a PK and a way to authenticate -- but in practice, a user object isn't much use unless you can say "Hello <user>", so I don't see why basic identity mechanisms shouldn't be part of the basic User contract (not necessarily defined at a data level, just at a data access level).
Sure. That's kind of why I defined an method for getting how to
display the user's name. I think that when you say "but in practice, a
user object isn't much use unless you can say "Hello <user>"", you are
really saying that the user isn't much without the profile data. It's
a very short step from wanting to display the user's name to wanting
to display his email or something else.
> Personally, I don't have a problem with UserProfile as a pattern, especially when it comes to per-app settings. However, the good news is that both approaches are possible once the User model is configurable. We can describe the approach of a generic User with UserProfile objects containing app settings; and also document the fact that if you have performance (or taste/convenience) concerns about the joins to a UserProfile object, you can define a per-project User object that incorporates all the profile data you want.
Yes. That's what I thought as well. There isn't stopping you from
using the user profile approach or even defining multiple profiles.
Thanks for taking a look at it,
Ian
--
Ian
--
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.
On Mon, Mar 19, 2012 at 1:00 AM, Joe Tennies <ten...@gmail.com> wrote:
> A feature I would love to see is the ability to support multiple forms of
> authentication for a single user account. (One account to many credentials.)
You can do this already with Django auth by specifying multiple
backends to the AUTHENTICATION_BACKENDS setting. Each one can take
different credentials but potentially return the same User.
See: https://docs.djangoproject.com/en/1.3/ref/settings/#std:setting-AUTHENTICATION_BACKENDS
--
Ian
Hi folks --This discussion of user authentication by email is getting pretty nasty; can we start over? I know there's a lot of quite legitimate frustration here, but we really need to drop the personal stuff and focus on the problem at hand. I want to move this forward quickly, and I can commit to spending some time on it in the coming weeks, but I'm going to lose interest faster than than you can believe if the tone doesn't improve. Please: keep it professional, and focus on the tech. I promise things'll go smoothly if we all do.As I see it, there's two basic approaches we could take:1. Incremental improvement: fix the most glaring issues with auth.User (starting with email addresses for login, clearly), and generally improve things a bit at a time. Once User's in a state where we're happy, move on the rest of the auth app -- again, a bit at a time. This approach would see the largest issues fixed more quickly, but would probably do so at the expense of code quality (e.g. requiring a one-off solution to schema migration of the User model) and would delay a more sweeping reform until later.2. Complete improvement: recognize that the auth app is fundamentally flawed, and mercilessly refactor/replace/rewrite it, all in one go. The hypothetical results here would be better -- a modern auth system unencumbered by the decisions we made in 2005 -- but this would take far longer, and would block on things like the app refactor and schema migrations.There's also a middle-ground proposal from Clay: make the auth app swappable, thus making it possible for *users* to replace the auth app while leaving time for us to make either incremental or complete change, as we see fit.I think we need to come together and agree on an approach before we move forward, so I'd like to see some concrete proposals for each of these approaches. Since all options have merits and since I think it's unlikely we'll find consensus I'm prepared to make a BDFL ruling here. So if you feel strongly about one approach or another, please write a concrete proposal and post it here or on the wiki. I'll look these over -- and also review Clay's branch -- and (again, baring consensus) make a ruling next week.Just so my biases are clear: normally I'd lean more towards the completionist stance, but in this case I haven't seen an actual proposal to completely replace auth. Further, I think the fact that it's blocked on *other* pretty hairy issues means it'd be unlikely to see that much action that quickly. I'm also somewhat opposed to the "pluggable auth" idea since I think it dilutes the utility of having built-in auth. In other words, if we're going to make auth up to users, why not just get rid of the built-in auth altogether? So I'm leaning towards an incremental improvement approach, but only if I can see a concrete proposal that articulates what to change, and deals with the backwards-compatibility issues in a not-too-ugly way.
Thanks!Jacob