1

我正在做一些感觉不是很有效率的事情。从下面的代码中,您可能会看到我正在尝试允许将多个不同类型的配置文件附加到我的自定义用户对象(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")
4

1 回答 1

2

首先,您可以通过不声明 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 已将其内置得更好,正如您所见。我还希望您在派生的配置文件类中确实有一些其他独特的字段,否则您应该将它们扔掉,然后将 usertype 字段传递到 BaseProfile 中(看看选择做这件事)。

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

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

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

于 2010-05-08T10:33:01.757 回答