【问题标题】:how to handle multiple profiles per user?如何处理每个用户的多个配置文件?
【发布时间】:2010-05-08 05:28:06
【问题描述】:

我正在做一些感觉不是很有效率的事情。从下面的代码中,您可能会看到我正在尝试允许将多个不同类型的配置文件附加到我的自定义用户对象(Person)。其中一个配置文件将被视为默认配置,并且应该具有来自 Person 类的访问器。在配置文件中存储 is_default 字段似乎不是跟踪默认值的最佳方式,是吗?

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


class Person(User):

    public_name = models.CharField(max_length=24, default="Mr. T")

    objects = UserManager()

    def save(self):
        self.set_password(self.password)
        super(Person, self).save()


    def _getDefaultProfile(self):

        def_teacher = self.teacher_set.filter(default=True)
        if def_teacher: return def_teacher[0]

        def_student = self.student_set.filter(default=True)
        if def_student: return def_student[0]

        def_parent  = self.parent_set.filter(default=True)
        if def_parent:  return def_parent[0]

        return False
    profile = property(_getDefaultProfile)


    def _getProfiles(self):
        # Inefficient use of QuerySet here. Tolerated because the QuerySets should be very small.
        profiles = []
        if self.teacher_set.count(): profiles.append(list(self.teacher_set.all()))
        if self.student_set.count(): profiles.append(list(self.student_set.all()))
        if self.parent_set.count():  profiles.append(list(self.parent_set.all()))

        return profiles
    profiles = property(_getProfiles)




class BaseProfile(models.Model):

    person = models.ForeignKey(Person)
    is_default = models.BooleanField(default=False)

    class Meta:
        abstract = True


class Teacher(BaseProfile):
    user_type = models.CharField(max_length=7, default="teacher")


class Student(BaseProfile):
    user_type = models.CharField(max_length=7, default="student")


class Parent(BaseProfile):
    user_type = models.CharField(max_length=7, default="parent")

【问题讨论】:

    标签: django profiles


    【解决方案1】:

    首先,您可以通过不声明 BaseProfile 抽象来使事情变得更容易:

    from django.db import models
    from django.contrib.auth.models import User, UserManager
    
    class Person(User):
        public_name = models.CharField(max_length=24, default="Mr. T")
        objects = UserManager()
    
        def save(self):
            self.set_password(self.password)
            super(Person, self).save()
    
        def _getDefaultProfile(self):
            try:
                return self.baseprofile_set.get(default=True)
            except ObjectDoesNotExist:
                return False
        profile = property(_getDefaultProfile)
    
        def _getProfiles(self):
            return self.baseprofile_set.all()
        profiles = property(_getProfiles)
    
    class BaseProfile(models.Model):
    
        person = models.ForeignKey(Person)
        is_default = models.BooleanField(default=False)    
    
    class Teacher(BaseProfile):
        user_type = models.CharField(max_length=7, default="teacher")    
    
    class Student(BaseProfile):
        user_type = models.CharField(max_length=7, default="student")    
    
    class Parent(BaseProfile):
        user_type = models.CharField(max_length=7, default="parent")
    

    这样更好吗?无论如何,您的属性都不知道它们返回的是什么类型,因此抽象基类只会让您有令人难以置信的烦人开销。

    如果您现在想知道,既然我做了任何返回 BaseProfile 的东西,您怎么能从特定配置文件中获取数据?你可以这样做:

    try:
        #note the lowercase teacher referal
        print myuser.profile.teacher.someteacherfield 
    except Teacher.DoesNotExist:
        print "this is not a teacher object!"
    

    我也希望您不要将 user_type 字段仅用于此目的,因为 django 已将其内置得更好,正如您所见。我还希望您在派生的配置文件类中确实有一些其他独特的字段,否则您应该将它们扔掉,然后将用户类型字段传递到 BaseProfile 中(看看choices 这样做很好)。

    现在至于 is_default,恕我直言,这个方法和任何方法一样好。您总是可以尝试将自定义约束添加到您的 dbms 本身,说应该有 0 或 1 条包含相同 FK 和 is_default=True 的记录(没有 django 方法可以做到这一点)。我还要说的是,添加一个方法 make_default 并在该方法中确保 is_default 对于该人是唯一的(例如,首先在具有相同 FK 的所有配置文件上将 is_default 设置为 False)。这将为您节省很多可能的悲伤。您也可以在 BaseProfile 的 save() 方法中添加此检查。

    您可以这样做的另一种方法是将外键添加到指向默认配置文件的人员模型。虽然这将确保默认值在 django 级别上是唯一的,但它还可以提供数据的非规范化和损坏,甚至在更烦人的级别上,所以我不是它的忠实粉丝。但同样,如果您通过预定义的方法添加/删除/更新配置文件(现在会更复杂!)您应该是安全的。

    最后,也许你有充分的理由从 User 继承,但是扩展 User 功能的默认方式不是这个,它被描述为here

    【讨论】:

    • 我避免从 BaseProfile 中删除抽象,希望在模板中到达我的“类型化”配置文件之前不必遍历另一层。但是,我错过的那个难题的缺失部分是在模型上创建访问器,从而使该层从模板中变得模糊不清。现在我看到走这条路开辟了许多更好的方法来做到这一点,这正是我一直在寻找的。我将尝试这种方法,谢谢 Killian!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多