2010-05-08 4 views
1

Je fais quelque chose qui ne me semble pas très efficace. De mon code ci-dessous, vous pouvez probablement voir que j'essaie d'autoriser plusieurs profils de différents types attachés à mon objet utilisateur personnalisé (personne). Un de ces profils sera considéré comme un défaut et devrait avoir un accesseur de la classe Person. Stocker un champ is_default sur le profil ne semble pas être le meilleur moyen de garder une trace d'un défaut, n'est-ce pas?comment gérer plusieurs profils par utilisateur?

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") 

Répondre

2

d'abord tout ce que vous pourriez faire des choses beaucoup plus facile de ne pas déclarer abstraite la 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") 

La façon dont cela est plus agréable? Vos propriétés ne savaient pas de quel type elles retournaient, donc la base de données abstraite ne vous a fait qu'agiter un frais incroyable.

Si vous vous demandez maintenant comment diable vous pouvez obtenir les données à partir des profils spécifiques depuis que j'ai fait quelque chose retourné BaseProfile? Vous pouvez faire quelque chose comme ceci:

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

Aussi j'espère que vous n'avez pas utilisé le champ user_type uniquement à cette fin, parce que django a fait construire en mieux que vous pouvez voir. J'espère également que vous avez vraiment d'autres champs uniques dans vos classes de profil dérivées, car sinon vous devriez les jeter et juste après un champ Usertype dans BaseProfile (regardez choices pour faire ça bien).

Maintenant, quant à is_default, cette méthode est aussi bonne que tout. Vous pouvez toujours essayer d'ajouter des contraintes personnalisées à votre dbms lui-même, en disant qu'il pourrait y avoir 0 ou 1 enregistrements contenant le même FK et is_default = True (il n'y a pas de moyen django pour le faire). Ce que je dirais aussi, c'est d'ajouter une méthode make_default et dans cette méthode, assurez-vous que is_default est unique pour cette personne (par exemple en définissant d'abord is_default sur False sur tous les profils avec le même FK). Cela vous épargnera beaucoup de chagrin possible. Vous pouvez également ajouter cette vérification dans la méthode save() de BaseProfile.

Une autre façon de procéder consiste à ajouter une clé étrangère au modèle de personne qui pointe vers le profil par défaut. Bien que cela garantira que le défaut soit unique au niveau du django, il peut également entraîner la dénormalisation et la corruption de vos données, même à un niveau plus ennuyeux, donc je n'en suis pas un grand fan. Mais encore une fois, si vous faites tout ajouter/supprimer/mettre à jour des profils grâce à des méthodes prédéfinies (ce sera plus complexe maintenant!), Vous devriez être en sécurité. Enfin, vous avez peut-être de bonnes raisons d'hériter de l'utilisateur, mais la façon par défaut d'étendre la fonctionnalité de l'utilisateur n'est pas celle-ci, elle est décrite here.

+0

J'avais évité de retirer l'abstraction du BaseProfile dans l'espoir de ne pas avoir à traverser une autre couche avant d'atteindre mes profils 'typés' dans les templates. Cependant, la pièce manquante à ce casse-tête que j'ai raté était la création d'accesseurs sur le modèle qui obscurci cette couche à partir des modèles. Maintenant, je vois que cette route ouvre une foule de meilleures façons de le faire, ce qui est exactement ce que je cherchais. Je vais expérimenter avec cette approche, merci Killian! – Scott

Questions connexes