代码之家  ›  专栏  ›  技术社区  ›  shanyu

在Django中自定义用户类是否中断应用程序?

  •  3
  • shanyu  · 技术社区  · 15 年前

    假设我已经正确地对用户模型(customuser)进行了子类化(如下所述: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/ )

    并安装了评论应用程序。

    要访问我编写的模板中的注释用户,请执行以下操作:

    {{comment.user}} # which provides User, not my CustomUser
    

    因此,

    {{comment.user.CustomProperty}} #does not work.
    

    我该怎么解决呢?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Community skywinder    7 年前

    这个 ForeignKey comments.Comment 是为了Django的内置 User 对象,因此查询 comment.user 将为您提供父对象(即:基 用户 模型)。但是,django继承提供了一种从超类获取子类版本的方法:

    {{ comment.user.customeruser }}
    

    然后你可以这样做:

    {{ comment.user.customeruser.customproperty }}
    

    我碰巧认为这是Django继承实现中的一个弱点,因为它并不完全反映您通常期望在Python中从对象继承得到的行为,但至少有一个解决方法。因为我几乎不急于用我的正确行为版本提交补丁,所以我不能抱怨:—)


    我同意Carl Meyer的意见:在不更改父模型的DB表的情况下自动获取子类,并从父类查询返回子类的实例可能会很昂贵,这与Django关于查询集返回运行该查询集的模型的承诺不一致。

    然而,我在实践中仍然发现,姜戈的继承有时会导致一些尴尬的额外步骤。从0.91开始使用了django,并且知道解决对象关系映射问题的所有不同策略都有权衡,我很高兴现在在django继承,并且觉得当前的实现非常出色…所以我不喜欢我最初的回答被认为是对这个项目的轻微反对。

    因此,我想我会编辑这个答案以链接到Carl自己在解决方案中提供的答案,如果您不知道子类是什么类型: How do I access the child classes of an object in Django without knowing the name of the child class? . 他在那里提供了使用ContentType框架的建议。仍然有一些间接的影响,但是工具箱中有一个好的、可归纳的选项。

        2
  •  2
  •   vikingosegundo    15 年前

    正如你在那篇文章的评论中看到的,这仍然是有争议的讨论,什么是最好的方法。

    我也尝试了子类化,但是我遇到了很多问题,而 using profiles 非常适合我。

    class IRCUser(models.Model):
        user        = models.ForeignKey(User, unique=True)
        name        = models.CharField(max_length=100, blank= True, null = True )
        friends     = models.ManyToManyField("IRCUser", blank= True, null = True)
        dataRecieved= models.BooleanField(default=False)
    

    创建IRCUSER的工作方式如下:

    >>> IRCUser(user = User.objects.get(username='Kermit')).save()
    

    编辑:为什么用户配置文件优雅:

    假设,我们正在编写一个webapp,它将表现为多协议聊天。用户可以在icq、msn、jabber、facebook、google talk…..

    我们可以通过继承自由地创建一个自定义用户类,它将保存所有附加信息。

    class CustomUser(User):
        irc_username = models.CharField(blank=True, null=True)
        irc_password = models.PasswordField(blank=True, null=True)
        msn_username = models.CharField(blank=True, null=True)
        msn_password = models.PasswordField(blank=True, null=True)
        fb_username = models.CharField(blank=True, null=True)
        fb_password = models.PasswordField(blank=True, null=True)
        gt_username = models.CharField(blank=True, null=True)
        gt_password = models.PasswordField(blank=True, null=True)
        ....
        ....
    

    这导致

    • 具有大量零值的数据行
    • 狡猾的 如果那时 验证
    • 不可能在同一个服务中拥有多个帐户

    现在让我们用用户配置文件来做

    class IRCProfile(models.Model):
        user = models.ForeignKey(User, unique=True, related_name='ircprofile')
        username = models.CharField()
        password = models.PasswordField()
    
    class MSNProfile(models.Model):
        user = models.ForeignKey(User, unique=True, related_name='msnprofile')
        username = models.CharField()
        password = models.PasswordField()
    
    class FBProfile(models.Model):
        user = models.ForeignKey(User, unique=True, related_name='fbprofile')
        username = models.CharField()
        password = models.PasswordField()
    

    结果:

    • 需要时可以创建用户配置文件
    • DB没有被零值淹没
    • n同一类型的配置文件可以分配给一个用户
    • 验证很容易

    这可能导致模板中的语法更加神秘,但是我们可以在视图/模板标记中使用一些快捷方式,或者使用 {% with ... %} 按照我们的口味来调味。

        3
  •  1
  •   Arnaud    15 年前

    我不认为有办法解决这个问题,因为正如你所说, comment.user 是一个 User 不是 CustomUser .

    http://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models :

    查询集仍返回请求的模型
    无论何时查询用户对象,都无法让django返回myuser对象。用户对象的查询集将返回这些类型的对象。代理对象的关键是依赖于原始用户的代码将使用这些对象,而您自己的代码可以使用包含的扩展(其他代码都不依赖)。这不是一种用你自己创造的东西来取代任何地方的用户(或任何其他)模型的方法。

    也许在这种情况下,旧的使用方法 UserProfile 会是更好的选择吗?

    对不起,如果我没帮什么忙。