【问题标题】:Extending auth.User model, proxied fields, and Django admin扩展 auth.User 模型、代理字段和 Django 管理员
【发布时间】:2011-07-09 15:31:54
【问题描述】:

(编辑:我知道 Django 中有一个完全独立的功能,称为“代理模型”。该功能对我没有帮助,因为我需要能够向 UserProfile 添加字段。)

所以我开始了一个新的 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 shell 中演示:

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

经过一番挖掘,看起来我想为 UserProfile._meta 覆盖 django.db.models.options.Options.get_field。但似乎没有一种非 hacky 的方式来做到这一点(我现在有一个非常 hacky 的解决方案,其中涉及猴子修补 UserProfile._meta.[get_field, get_field_by_name])......有什么建议吗?谢谢。

【问题讨论】:

  • 刚刚更新,注意到我有一个 hacky 解决方案。但如果它存在的话,一个非 hacky 的会很棒。
  • 哦,顺便说一下,向我们展示您的admin.py 文件可能在这里有用=P

标签: python django django-models django-admin proxy-classes


【解决方案1】:

保持简单。下面是我们使用的库中的 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)

这允许您对 User 对象进行 CRUD,并以内联方式访问 UserProfile。现在您不必将来自 UserProfile 的属性查找代理到 User 模型。要从User u 的实例访问UserProfile,请使用u.get_profile()

【讨论】:

  • 如果您希望 get_profile() 工作,您还需要将 settings.py 中的 AUTH_PROFILE_MODULE 设置设置为正确的模型。
  • 谢谢,这确实解决了管理问题,但每当我想访问配置文件上的用户属性时,我不得不处理烦人的额外 .user。我有另一个代码库,在那里我经常做profile.user.Xuser.get_profile().Y 之类的事情,并且在这个新代码库中,我认为在一个地方有点__getattr__ 丑陋可能值得在其他地方增加一致性。如果不出意外,这将是开发过程中生产力的提高。
  • 无论如何,用户对象上只有几个字段。只需在委托给用户的用户配置文件上定义一些@properties。出于某种原因,我真的不喜欢__getattr__ 方法。
【解决方案2】:

这不是代理类,它是一种关系。在Proxy Models 上查看更多信息,它是原始模型的子类,Meta.proxy = True

【讨论】:

  • 是的,我刚刚更新了标题,因为我意识到它可能会导致这种混乱。我知道“代理模型”是另一回事,但在这种情况下它们对我没有帮助,因为我希望能够向 UserProfile 添加字段。所以虽然你说得对,我的标题本来可以更清楚,但我的问题仍然存在。
【解决方案3】:

如果您只希望 User 的字段显示在 UserProfileAdmin 的 list_display 中,请尝试:

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

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

【讨论】:

  • 谢谢,但是像这样在 list_display 中使用字段查找实际上是行不通的,至少在 Django 1.2.5 中不行。
猜你喜欢
  • 1970-01-01
  • 2012-09-18
  • 2014-12-01
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 2019-07-05
  • 2017-04-10
  • 2021-12-11
相关资源
最近更新 更多