扩展auth.User模型,代理字段和Django admin

问题描述:

(编辑:我知道在Django中有一个完全独立的功能叫做“代理模型”,该功能对我没有帮助,因为我需要能够添加字段到UserProfile。)扩展auth.User模型,代理字段和Django admin

因此,我开始一个新的Django应用程序,并且创建一个UserProfile模型,它是django.contrib.auth.models.User的扩展,并将失败的属性请求返回给用户,如下所示:

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) 

这工作总体正常,但休息时,我尝试在UserProfileAdmin.list_display使用User场。问题出在这里的管理员验证代码:

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: 
          opts.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)) 

问题是,虽然UserProfile的一个实例将有代理字段,例如,电子邮件,UserProfile类本身不。在全世界展示Django的外壳:

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

一些挖后,它看起来像我要重写django.db.models.options.Options.get_field为UserProfile._meta。但似乎没有这样做(我现在有一个非常hacky的解决方案,它涉及猴子补丁UserProfile._meta。[get_field,get_field_by_name])...任何建议?谢谢。

+0

刚刚更新以说明我有一个hacky解决方案。但是如果它存在的话,一个非哈克的人会很棒。 – rfrankel 2011-03-06 03:04:33

+0

哦,顺便说一句,告诉我们你的'admin.py'文件可能在这里很有用= P – 2011-03-06 05:50:38

这不是代理类,它是一种关系。查看更多关于Proxy Model s,这是原始模型的一个子类,与Meta.proxy = True

+0

是的,我刚刚更新了标题,因为我意识到它可能会造成这种混淆。我知道“代理模型”是一个不同的东西,但在这种情况下他们不帮助我,因为我希望能够将字段添加到UserProfile。所以,虽然你说得对,我的标题可以更清楚,但我的问题是。 – rfrankel 2011-03-06 02:07:53

如果你只是想从用户的字段是在你UserProfileAdmin list_display,尝试:

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

如果你想将其作为表单的一部分,将其作为额外字段添加到UserProfileForm中,并在表单中对其进行验证。

+1

谢谢,但在list_display中使用字段查找实际上不起作用,至少在Django 1.2.5中不起作用。 – rfrankel 2011-03-07 05:11:36

保持简单。以下是我们使用的库中UserProfile模型的一个示例:

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

就是这样。不要打扰__getattr__覆盖。自定义管理界面来代替:

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) 

这可以让你的CRUD操作对象User与访问用户配置为内联。现在,您不必从UserProfile代理用户模型的属性查找。要从User u的实例中访问UserProfile,请使用u.get_profile()

+0

如果你想让get_profile()工作,你需要将settings.py中的AUTH_PROFILE_MODULE设置设置为正确的模型。 – Cromulent 2011-03-06 12:48:24

+0

谢谢,这确实解决了管理问题,但是当我想要访问配置文件上的用户属性时,我不得不处理烦人的额外'.user'。我有另一个代码库,我经常在做'profile.user.X'或'user.get_profile().'这样的事情,并且在这个新的代码库中,我认为在一个地方有一点'__getattr__'可能是值得的在其他地方增加了一致性。如果没有其他的话,它会在开发过程中提高生产率。 – rfrankel 2011-03-07 05:15:00

+1

无论如何,用户对象只有几个字段。只需在委托给用户的userprofile上定义一些@properties。出于某种原因,我真的不喜欢'__getattr__'方法。 – 2011-03-07 12:25:33