如何处理每个用户的多个配置文件?
我正在做一些感觉效率不高的事情。从下面的代码中,您可能会看到我正在尝试允许将不同类型的多个配置文件附加到我的自定义用户对象(人)。这些配置文件之一将被视为默认配置文件,并且应该具有来自 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")
I'm doing something that doesn't feel very efficient. From my code below, you can probably see that I'm trying to allow for multiple profiles of different types attached to my custom user object (Person). One of those profiles will be considered a default and should have an accessor from the Person class. Storing an is_default
field on the profile doesn't seem like it would be the best way to keep track of a default, is it?
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")
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您可以通过不声明 BaseProfile 摘要来使事情变得更加容易:
这样更好吗?无论如何,您的属性都不知道它们返回什么类型,因此抽象基类只会让您产生令人难以置信的烦人开销。
如果您现在想知道自从我做了任何返回的 BaseProfile 以来,您到底如何从特定配置文件中获取数据?你可以这样做:
另外,我确实希望你不要仅将 user_type 字段用于此目的,因为正如你所看到的,django 已经更好地内置了它。我还希望您在派生的配置文件类中确实有一些其他独特的字段,否则您应该将它们扔掉,然后将用户类型字段传递到 BaseProfile 中(查看 选择 来做到这一点)。
现在至于 is_default,恕我直言,这个方法和任何方法一样好。您始终可以尝试向 dbms 本身添加自定义约束,表示应该有 0 或 1 条记录包含相同的 FK 且 is_default=True (没有 django 方法可以做到这一点)。我还要说的是,添加一个 make_default 方法,并在该方法中确保 is_default 对于该人来说是唯一的(例如,首先在具有相同 FK 的所有配置文件上将 is_default 设置为 False)。这将为您省去很多可能的悲伤。您还可以在 BaseProfile 的 save() 方法中添加此检查。
另一种方法是向人员模型添加一个指向默认配置文件的外键。虽然这将确保默认值在 django 级别上是唯一的,但它也可能会导致数据的非规范化和损坏,甚至在更烦人的级别上也是如此,所以我不太喜欢它。但同样,如果您通过预定义的方法添加/删除/更新配置文件(现在会更复杂!),您应该是安全的。
最后,也许您有充分的理由从 User 继承,但扩展 User 功能的默认方式不是这样的,它被描述为 此处。
First of all you could make things a lot more easy by not declaring the BaseProfile abstract:
The way this is nicer? Your properties didn't know anyway what type they were returning, so the abstract baseclass only made you have an incredible annoying overhead there.
If you now are wondering how the hell you can get the data from the specific profiles since I made anything returned BaseProfile? You can do something like this:
Also I do hope you didn't use the user_type field solely for this purpose, because django has it built in better as you can see. I also hope you really have some other unique fields in your derived profile classes because otherwise you should throw them away and just past a usertype field into BaseProfile (look at choices to do this good).
Now as for the is_default, imho this method is as good as any. You can always try to add custom constraints to your dbms itself, saying there sould be 0 or 1 records containing the same FK and is_default=True (there is no django way to do this). What I also would say is, add a method make_default and in that method make sure the is_default is unique for that person (e.g. by first setting is_default to False on all profiles with the same FK). This will save you a lot of possible sorrow. You can also add this check in the save() method of BaseProfile.
Another way you could do it is by adding a Foreign Key to the Person Model that points to the default Profile. While this will ensure default to be unique on django level, it can also provide denormalization and corruption of your data, even on a more annoying level, so I'm no big fan of it. But again, if you do all adding/removing/updating of profiles through predefined methods (will be more complex now!) you should be safe.
Finally, maybe you have good reasons to inherit from User, but the default way to extend the User functionality is not this, it's described here.