19

I have the same conundrum as presented in this question, but applied to Django's auth.User.

I have this proxy model:

class OrderedUser(User):
    def __unicode__(self):
        return self.get_full_name()

    class Meta: 
        proxy=True
        ordering=["first_name", "last_name"]

And some of my other models use an OrderedUser instead of a django.contrib.auth.models.User as field types.

In my views I then use the request.user to populate a field and - as expected - get an error:

'Cannot assign "<User...>": <field> must be a "OrderedUser" instance'

I could just do OrderedUser.objects.get(request.user.id), but that is an extra hit to the database.

So, how to convert a base model class into its proxied class?

Community
  • 1
  • 1
cethegeek
  • 6,286
  • 35
  • 42

2 Answers2

17

It's another database hit, but this will work:

OrderedUser.objects.get(pk=request.user.pk)

Edit You could try:

o = OrderedUser()
o.__dict__ = request.user.__dict__
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 1
    Right. Is there a mechanism that doesn't involve a hit to the DB? – cethegeek Jun 09 '10 at 14:34
  • 3
    Thanks for this tip. One thing I've found though, when using this in a context_processor to pass a user object to the templates... you need to do it differently if the user isn't logged in. As you have it, o always ends up as an OrderedUser object (descended from User), but if the user isn't logged in, it should be of the type AnonymousUser. So I would test if `request.user.is_authenticated()` and if so, only then do the above. Else, do `o = request.user`. – Phil Gyford Aug 25 '11 at 10:45
16

I couldn't get the copy dict method to work on Python 2.7.4 and Django 1.6. I didn't trace it all the way down, but I think it had something to do with it being a lazy object.

What worked for me:

request.user.__class__ = OrderedUser

It avoids the database call and gives access to the base auth stuff and all my extended behaviors.

To make the request.user act like the proxy class always, I put the assignment in middleware after the authentication middleware and before any of my apps could reference it. Since the middleware is called on every request, make sure the user is authenticated before setting the class. Using your example, here is how the middleware code might look:

from yourapp.models import OrderedUser

class OrderedUserMiddleware():
    def process_request(self, request):
        if hasattr(request, 'user') and request.user.is_authenticated():
            request.user.__class__ = OrderedUser

And remember to register the middleware after the authentication:

MIDDLEWARE_CLASSES = [
    # ...middleware from the guts of Django, none of your middle wares yet...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # ...more middleware from the guts of Django, none of your middlewares yet...
    'yourapp.middleware.OrderedUserMiddleware',
    # ...more middleware from you other middlewares...
]
FlyDanoFly
  • 269
  • 2
  • 4