【问题标题】:Django: Automatically selecting related model UserProfile when retrieving User modelDjango:检索用户模型时自动选择相关模型UserProfile
【发布时间】:2018-03-06 15:44:44
【问题描述】:

在 Django 模板中显示{{ user }} 时,默认行为是显示用户名,即user.username

我将其更改为显示用户的姓名首字母,它们存储在单独的 (OneToOneField) UserProfile 模型中。

所以在customsignup/models.py 中,我已经成功地覆盖了__unicode__ 函数,得到了想要的结果:

# __unicode__-function overridden.
def show_userprofile_initials(self):
    return self.userprofile.initials
User.__unicode__ = show_userprofile_initials

当然,数据库再次受到打击,因为每次要求user 对象显示为字符串时,它都需要独立选择UserProfile 模型。因此,即使这可行,它也会使数据库命中的数量增加不少。

所以我想做的是,每当从数据库调用User 模型时自动使用select_related('userprofile'),因为我首先在与用户打交道时基本上总是需要配置文件。

在更专业的术语中,我试图覆盖现有模型的模型管理器。所以我无法控制 User 模型定义本身,因为它在导入的库中。

所以我尝试以与覆盖__unicode__ 函数相同的方式覆盖User 模型的objects 成员,如下所示:

# A model manager for automatically selecting the related userprofile-table
# when selecting from user-table.
class UserManager(models.Manager):
    def get_queryset(self):
        # Testing indicates that code here will NOT run.
        return super(UserManager, self).get_queryset().select_related('userprofile')
User.objects = UserManager()

这应该有效吗?如果是这样,我做错了什么?

(如果答案能表明这首先不应该起作用,我会将答案标记为正确。)

我在这里发现了一个类似的问题,但它是从另一端接近的: Automatically select related for OneToOne field

【问题讨论】:

  • 为什么不只是user.profile.initials?在您的配置文件模型上创建 OneToOne 字段还会为相关模型的实例创建反向访问器。您可以通过配置文件模型字段上的 related_name 关键字参数指定反向访问器名称。例如user = models.OneToOneField('auth.User', related_name='profile')
  • 我通常会避免在 __str__/__unicode__ 方法中使用外键。如果您总是想要显示首字母而不是用户名,这可能表明首字母应该是自定义User 模型上的字段。即使您不这样做,如果您刚刚开始您的项目,创建自定义用户模型也是一个好主意。这样您就可以使用您的自定义管理器,而不是尝试修补默认的 User 模型。

标签: python django django-models


【解决方案1】:

不,User.objects = MyManger() 不应该工作。 According to the docs,只有两种支持的方法来扩展提供的 auth User 模型,一种是 profile 模型,就像你正在做的那样,或者是一个 proxy 模型,这可能不会不适合你的情况。来自文档(强调添加):

有两种方法可以在不替换您自己的模型的情况下扩展默认用户模型。如果您需要的更改纯粹是行为性的,并且不需要对数据库中存储的内容进行任何更改,则可以基于 User 创建一个代理模型。这允许代理模型提供的任何功能,包括默认排序、自定义管理器或自定义模型方法。

如果您希望存储与用户相关的信息,您可以将 OneToOneField 用于包含字段的模型以获取更多信息。这种一对一模型通常称为个人资料模型,因为它可能存储与网站用户无关的身份验证信息

作为扩展提供的身份验证用户模型的替代方法,您可以provide your own custom User model。然后你就可以完全控制它的管理者了。

相反,请考虑将{{ user }} 替换为{{ user.profile.initials }}。在您的配置文件模型上创建 OneToOne 字段还会为相关模型的实例创建反向访问器。您可以通过配置文件模型字段上的 related_name 关键字参数指定反向访问器名称。比如……

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

class UserProfile(models.Model)
    user = models.OneToOneField('auth.User', related_name='profile')
    initials = models.CharField(max_length=6)

some_user = User.objects.first()
# assuming there is already a profile related to this user
some_user.profile.initials = 'S.P.Y.'

您还可以为您的个人资料模型创建一个__str__ 方法,例如

def __str__(self):
    return self.initials

然后当你在模板中执行{{ user.profile }} 时,会显示首字母。

【讨论】:

  • 感谢您的回答。尝试以我在问题中描述的方式进行操作的唯一目的是,我希望在替换 {{ user }} 实例时尽可能少地改变并避免错误,尤其是在性能优化方面。但我接受这个答案是正确的,虽然这一次我的野心似乎不会实现。 :) 如果我的尝试无论如何都是不可能的,那么您提出的解决方案是我期望的必要路径。再次感谢!
猜你喜欢
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 2017-04-16
  • 2017-11-07
  • 2017-05-14
相关资源
最近更新 更多