I consider the mixins I wrote at
https://github.com/ogier/django/blob/auth-mixins/django/contrib/auth/mixins.py<https://github.com/ogier/django/blob/auth-mixins/django/contrib/auth/mixins.py#L120>
helpful
because they decompose the old User model into some orthogonal chunks that
you can pick and choose from when writing your own user model. Russell went
a different direction, making a strictly increasing hierarchy of
functionality, AbstractBaseUser -> AbstractUser -> User.

Russell's method has some advantages. It's conceptually simpler, and python
has always handled multiple inheritance in a mildly broken way (namely, the
super(self, klass) hierarchy is wonky). Also, his contract for a User model
guarantees a single, unique field that is a primary identifier, which may
be nice for apps trying to depend on a user model. That said, I'm not a
huge fan of the lines Russell has chosen for his extensibility points.
However, he has certainly written many more Django websites than I have, so
maybe he has more insight into what is really needed.

Here's my justification for more fine-grained mixins:

- AbstractBaseUser isn't really minimal. It assumes a database-backed
password field for every user. This may or may not make sense: I have
written sites that use auth.User but do no internal authentication, instead
shelling out entirely to an external service like Kerberos, CAS, OpenID,
etc. It still is nice to have a password as a fallback, even if you don't
expose it to non-admins, so that if an external service is unavailable you
aren't locked out of the site, which may be enough justification for
leaving the password field and related authentication mechanisms on every
user.

- AbstractUser isn't very flexible. Since you can't actually override or
change the fields of an abstract model anyways (so far as I know?) the main
benefits the AbstractUser model gives you over the default User+Profile
model is that there is one less join when querying the database, and you
can avoid the ad-hoc get_profile() calls you previously needed to access
extra fields. You are still stuck with the small username field and
non-unique email addresses.

- AbstractUser is too heavy. In particular, it makes the same mistake that
auth.User does of conflating authorization with identity, when these are
actually entirely decomposable. In particular, the choice of identity as
email vs. username vs. Windows domain account vs. hidden auto-incrementing
primary key etc.... is totally orthogonal to the choice of authorization
mechanism as Django content-types/permissions vs. always-yes vs.
hand-rolled granular permissions etc....

If you want to codify that last point, I see two major types of mixins that
would useful when implementing a user class, corresponding to the two main
choices I have to make when implementing a site. How do my users identify
themselves? and How do I control what they are authorized to do? Currently,
if I choose anything besides "My users use a username and password to log
in" for identity, I am forced to re-implement permissions from scratch,
which I probably don't want to do. And vice versa.

Moving forwards, I assume Russell is pretty invested in the current class
hierarchy. Since the Django codebase has started duck-typing the User model
anyways, I could certainly reimplement the mixin hierarchy as a reusable
app, though it would be the opposite of DRY since 90% of the code is
duplicated exactly. We could also merge the two, and implement AbstractUser
as inheriting AbstractBaseUser + UsernameMixin + PermissionsMixin. I might
try that on my branch, and see how it works out. My guess is it will work
just fine albeit with an absurdly deep inheritance tree.

Best,
Alex Ogier


On Tue, Nov 6, 2012 at 1:11 AM, Ryan Kaskel <[email protected]> wrote:

> I implemented a custom User model in my new project so I can sympathize
> with Ionel a bit. I wanted to change a few fields on User and have an admin
> with the same functionality as the 1.4 auth.User.
>
> This necessitated some copying and pasting to define the User's relations
> to auth.Group and auth.Permission (as well as adding is_staff,
> is_superuser, and a few help methods related to perms such as
> get_group_permissions).
>
> I also had to "re-implement" (i.e. copy, paste, and change a few fields)
> auth.User's ModelAdmin to get the admin interface looking similar (this
> included some related forms).
>
> Still though, the benefit of Russ's additions far outweigh some of these
> minor conveniences, which, to be fair, he mentioned in his draft branch
> release notes (June 4 -
> https://groups.google.com/d/msg/django-developers/YwEZUIMuIkE/g9-tAZ15RxMJ
> ):
> """
>  * The auth forms are bound to User, and don't adapt to MyUser.
>
>  * You need to roll your own admin registration for MyUser
>
>  * The admin template still asks for "username", even if the email
> address is the identity field.
>
> ....
>
>  * A full audit of admin to formally establish the "admin User" interface.
>
>  * Possibly factor out the fields on User that relate to Permissions,
> so it's easy to add the full set of admin permission bits to a custom
> User model. I know Alex Ogier's branch has done some work on these
> last two points, so that's probably where I'll start (or if someone
> wants to beat me to it…)
> """
>
> Looks like someone needs to take the helm and implement some mixins. Maybe
> start it as a reusable app and grandmother it into Django? Or use Alex's
> work (
> https://github.com/ogier/django/blob/auth-mixins/django/contrib/auth/mixins.py#L120
> )?
>
> -- Ryan
>
>
> On Tuesday, 6 November 2012 02:19:47 UTC, donarb wrote:
>>
>> I think it's basically a bit of an ambiguous reading of that section of
>> the documentation:
>>
>> *"If you want your custom User model to also work with Admin, your User
>> model must define some additional attributes and methods. These methods
>> allow the admin to control access of the User to admin content:"*
>>
>> It's saying that the admin app uses those attributes and methods to
>> determine access. What this means is that they are already defined in
>> AbstractUser, but you can override them if your user has a different
>> definition of is_staff or is_active or the permissions. If you look at the
>> source for AbstractUser, it uses the built-in groups and permissions, just
>> as the old User did.
>>
>> Don
>>
>> On Monday, November 5, 2012 5:22:51 PM UTC-8, Ionel Cristian Mărieș wrote:
>>>
>>> I think you are referring to
>>> https://docs.djangoproject.**com/en/dev/topics/auth/#**
>>> custom-users-and-django-**contrib-admin<https://docs.djangoproject.com/en/dev/topics/auth/#custom-users-and-django-contrib-admin>
>>> But I don't want to re-implement those if they already exist on
>>> AbstractUser. I want exactly the same permission granularity as with
>>> non-custom User so I have to pull in the groups and permission
>>> relations.
>>>
>>> Maybe there should be a mixin with those ?
>>>
>>> And I'm not saying the admin doesn't work with AbstractBaseUser, I'm
>>> just implying that's too much code needed to get the equivalent admin
>>> features on a custom user. Maybe my usecase is not that common and I
>>> need to accept the fact, but then again, maybe not. I'm just putting
>>> it on the table.
>>>
>>> Thanks,
>>> -- ionel
>>>
>>> On Nov 6, 3:04 am, Russell Keith-Magee <[email protected]>
>>> wrote:
>>> > On Tue, Nov 6, 2012 at 7:59 AM, ionel <[email protected]> wrote:
>>> > > Hello,
>>> >
>>> > > I'm trying to make a custom user model so I can login and register
>>> > > with the email (instead of username). This means the email must
>>> become
>>> > > unique and I don't need the username fields. I don't want to fill it
>>> > > with random data, unique ids, hashes or whatnot.
>>> >
>>> > > Also, I need to have the admin working. Because of this I'm forced
>>> to
>>> > > use the AbstractUser instead of AbstractBaseUser.
>>> >
>>> > Incorrect. I've got several examples -- and the documentation contains
>>> a
>>> > fully worked example -- of a User model extending AbstractBaseUser,
>>> using
>>> > email address for a login, that works with admin. I'm also using an
>>> example
>>> > of a User model that uses email address as a login on a site I'm
>>> currently
>>> > developing, and so far, it's working fine.
>>> >
>>> > > It seems to me that
>>> > > this abstract is concerned with two purposes: fields and methods for
>>> > > the admin and display fluff like username, email, first_name,
>>> > > last_name. This seems wrong to me, there should be two abstract
>>> > > models: BaseAbstractAdminUser and AbstractUser.
>>> >
>>> > > The other alternative would be allowing abstract model subclasses to
>>> > > override fields. Not sure about this tho, why is this disallowed in
>>> > > the first place?
>>> >
>>> > > What do you think ?
>>> >
>>> > It's not clear to me what exactly your proposing, and how much of that
>>> > stems from a mistake in your original premise.
>>> >
>>> > Like I said -- admin *does* work with AbstractBaseUser subclasses, and
>>> the
>>> > documentation describes how to do it.
>>> >
>>> > If you're following that documentation and your hitting specific
>>> problems,
>>> > then you may have found a bug, and I'm certainly interested in hearing
>>> what
>>> > those problems are.
>>> >
>>> > Yours,
>>> > Russ Magee %-)
>>>
>>  --
> 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/-/7C2RlNKxFGAJ.
>
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to