开发者

Extending auth.User model, proxied fields, and Django admin

(Edit: I know that there's a totally separate feature in Django called "Proxy Models". That feature doesn't help me, because I need to be able to add fields to UserProfile.)

So I'm starting a new Django app and I'm creating a UserProfile model which is a extension of django.contrib.auth.models.User and failed attribute requests back to User, as follows:

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile')

    def __getattr__(self, name, *args):
        if name == 'user' or name == '_user_cache':
            raise AttributeError(name)

        try:
            return getattr(self.user, name, *args)
        except AttributeError, e:
            raise AttributeError(name)

This works fine in general, but breaks when I try to use a User field in UserProfileAdmin.list_display. The problem is in the admin validation code here:

def validate(cls, model):
    """
    Does basic ModelAdmin option validation. Calls custom validation
    classmethod in the end if it is provided in cls. The signature of the
    custom validation classmethod should be: def validate(cls, model).
    """
    # Before we can introspect models, they need to be fully loaded so that
    # inter-relations are set up correctly. We force that here.
    models.get_apps()

    opts = model._meta
    validate_base(cls, model)

    # list_display
    if hasattr(cls, 'list_display'):
        check_isseq(cls, 'list_display', cls.list_display)
        for idx, field in enumerate(cls.list_display):
            if not callable(field):
                if not hasattr(cls, field):
                    if not hasattr(model, field):
                        try:
                            o开发者_JAVA百科pts.get_field(field)
                        except models.FieldDoesNotExist:
                            raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))

The problem is that while an instance of UserProfile will have proxied fields, e.g. email, the UserProfile class itself doesn't. Demoed in the Django shell:

>>> hasattr(UserProfile, 'email')
False
>>> hasattr(UserProfile.objects.all()[0], 'email')
True

After some digging, it looks like I'd want to override django.db.models.options.Options.get_field for UserProfile._meta. But there doesn't seem to be a non-hacky way to do this (I have a very hacky solution right now, which involves monkey-patching UserProfile._meta.[get_field, get_field_by_name])...any suggestions? Thanks.


Keep it simple. Here's an example of a UserProfile model from a library we use:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    accountcode = models.PositiveIntegerField(null=True, blank=True)

That's it. Don't bother with the __getattr__ override. Customise the admin interface instead:

from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

class UserProfileInline(admin.StackedInline):
    model = UserProfile

class StaffAdmin(UserAdmin):
    inlines = [UserProfileInline]
    # provide further customisations here

admin.site.register(User, StaffAdmin)

This allows you to CRUD the User object, with access to the UserProfile as an Inline. Now you don't have to proxy attribute lookups from the UserProfile to the User model. To access the UserProfile from an instance of User u, use u.get_profile()


This is not a proxy class, it is a relationship. See more on Proxy Models, which are a subclass of the original model, with the Meta.proxy = True


If you just want a field from User to be in list_display in your UserProfileAdmin, try:

class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('user__email',)

If you want to have it as part of the form, add it into your UserProfileForm as an extra field, and validate it in the form.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜