2010-05-08 3 views
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") 

ответ

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 исключительно для этой цели, потому что Джанго была она построена в лучше, как вы можете видеть. Я также надеюсь, что у вас действительно есть другие уникальные поля в ваших производных классах профилей, потому что иначе вы должны выбросить их и просто пройти через поле usertype в BaseProfile (посмотрите на choices, чтобы сделать это хорошо).

Теперь, что касается is_default, imho этот метод не хуже любого. Вы всегда можете попытаться добавить пользовательские ограничения к самим dbms, указав, что должно быть 0 или 1 записи, содержащие те же FK и is_default = True (для этого нет способа django). Я бы тоже сказал, добавьте метод make_default и в этом методе убедитесь, что is_default уникален для этого человека (например, сначала установите is_default на False для всех профилей с тем же FK). Это сэкономит вам много возможной печали. Вы также можете добавить эту проверку в метод save() BaseProfile.

Другой способ, которым вы могли бы это сделать, - добавить внешний ключ к модели Person, который указывает на профиль по умолчанию. Хотя это обеспечит уникальность по умолчанию на уровне django, оно также может обеспечить денормализацию и повреждение ваших данных, даже на более раздражающем уровне, поэтому я не являюсь большим поклонником этого. Но опять же, если вы все добавляете/удаляете/обновляете профили через предопределенные методы (теперь будет сложнее!), Вы должны быть в безопасности.

Наконец, может быть, у вас есть веские причины унаследовать от пользователя, но способ расширения функций пользователя по умолчанию - это не так, это описано here.

+0

Я избегал удаления абстракции из BaseProfile в надежде, что вам не придется проходить через другой слой, прежде чем попасть в мои «типизированные» профили, находясь в шаблонах. Однако недостающую часть этой головоломки, которую я пропустил, это создание аксессуаров на модели, которая скрывала этот слой от шаблонов. Теперь я вижу, что на этом пути открывается множество лучших способов сделать это, и именно этого я и искал. Я буду экспериментировать с этим подходом, спасибо Киллиан! – Scott

Смежные вопросы