Django Multi-Table Héritage VS Spécification explicite OneToOne Relation dans les modèles

StackOverflow https://stackoverflow.com/questions/1796556

  •  22-09-2019
  •  | 
  •  

Question

Je espère que tout cela a du sens :) Je vais clarifier via les commentaires si nécessaire. De plus, je suis expérimenté en utilisant du texte en gras dans cette question, et je le modifier si je (ou vous) trouve distrayant. Avec cela à la façon ...

Utilisation django.contrib.auth nous donne utilisateurs et des groupes, entre autres choses utiles que je ne peux pas faire sans (comme la messagerie de base).

Dans mon application, j'ai plusieurs types d'utilisateurs différents. Un utilisateur peut être d'un seul type. Ce serait facilement assuré par des groupes, avec un peu de soins. Cependant, ces différents utilisateurs sont liés les uns aux autres dans les hiérarchies / relations.

Jetons un coup d'oeil à ces utilisateurs: -

directeurs d'école - utilisateurs "haut niveau"

Les administrateurs - chaque administrateur rapporte à un principal

coordinateurs - chaque rapport coordonnateur à un administrateur

En dehors de ces il existe d'autres types d'utilisateurs qui ne sont pas directement liés , mais peut se liés plus tard. Par exemple, « Société » est un autre type d'utilisateur, et peut avoir différents « produits », et des produits peut être supervisé par un « coordonnateur ». « Acheteur » est un autre type d'utilisateur qui peuvent acheter des produits.

Maintenant, tous ces Les utilisateurs ont d'autres attributs, dont certains sont communs à tous les types d'utilisateurs et dont certains sont distincts à un seul type d'utilisateur . Par exemple, tous les types d'utilisateurs doivent avoir une adresse. D'autre part, seul l'utilisateur principal appartient à un « BranchOffice ».

Un autre point qui a été dit plus haut, est que Un utilisateur ne peut jamais être d'un type .

L'application a également a besoin de garder une trace de qui a créé et / ou modifiés directeurs, administrateurs, coordinateurs, sociétés, produits etc . (C'est donc deux liens vers le modèle de l'utilisateur.)

Dans ce scénario, est-ce une bonne idée d'utiliser l'héritage multi-table de Django comme suit: -

from django.contrib.auth.models import User
class Principal(User):
    #
    #
    #    
    branchoffice = models.ForeignKey(BranchOffice)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
    #
    #
    #

Ou devrais-je aller à le faire comme ceci: -

class Principal(models.Model):
    #
    #
    #
    user = models.OneToOneField(User, blank=True)
    branchoffice = models.ForeignKey(BranchOffice)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
    #
    #
    #

S'il vous plaît garder à l'esprit qu'il existe d'autres types d'utilisateurs qui sont liés par des clés étrangères, par exemple: -

class Administrator(models.Model):
    #
    #
    #
    principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
    user = models.OneToOneField(User, blank=True)
    province = models.ForeignKey(         Province)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")

Je suis conscient que Django utilise-t-un-à-un pour l'héritage multi-table dans les coulisses. Je suis tout simplement pas assez qualifié pour décider ce qui est une approche plus saine.

Était-ce utile?

La solution

Je voudrais développer la solution par @thornomad.

L'extension de la classe utilisateur Django peut directement causer toutes sortes de problèmes avec les mécanismes de django.auth internes. Ce que je l'ai fait dans une situation similaire est précisément ce que @thornomad suggère - je fait mon propre modèle UserProfile associé l'un à un avec le modèle utilisateur Django, dans lequel je tenais les données utilisateur supplémentaires et dont j'ai hérité des modèles pour différents types des utilisateurs.

Quelque chose à adapter ce que vous avez décrit:

class UserProfile(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    class Meta:
        abstract = True


class PositionHolderUserProfile(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")

class Principal(PositionHolderUserProfile):
    branchoffice = models.ForeignKey(BranchOffice)

class Administrator(PositionHolderUserProfile):
    superior = models.ForeignKey(Principal, related_name="subordinates")
    province = models.ForeignKey(Province)

class Coordinator(PositionHolderUserProfile):
    superior = models.ForeignKey(Administrator, related_name="subordinates")


class Company(UserProfile):
    name = models.CharField(max_length=50)

class Product(models.Model):
    name = models.CharField(max_length=50)
    produced_by = models.ForeignKey(Company)

class Buyer(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    products_bought = models.ManyToManyField(Product)

Autres conseils

J'ai récemment basculé sur l'utilisation de modèles héritant hors de contrib.auto.models.User. Mon observation générale est que, en théorie, ils sont grands, mais parfois ils ne reçoivent pas automatiquement par magie traités comme ils sont censés le faire.

Je pense que votre décision en ce qui concerne l'héritage par rapport OneToOne se résume à ceci:

  • Est-ce que je veux avoir Django faire automatiquement quelque chose de bien 95% du temps, et la nécessité de déboguer que d'autres 5%

-OR -

  • Est-ce que je veux faire quelque chose moi-même manuellement 100% du temps

Si vous ne l'avez pas vu, le blog Scott Barham a un post sur héritant hors de l'utilisateur, et aussi la construction d'une coutume back-end pour vous assurer que votre objet personnalisé est retourné - Extension du Django utilisateur

De plus d'intérêt serait le champ AutoOneToOne fourni par django-ennuyeux . Il est une sorte d'hybride des deux approches ici -. Il n'y a pas lieu de prendre de l'héritage, mais Django prend soin de créer l'appariement OneToOneField si ce n'est pas présent

En outre, thornomad ne fait un bon point au sujet de la redondance dans vos modèles. Vous pouvez facilement mettre en œuvre une classe abstraite pour nettoyer que comme si (en supposant que vous vous faites OneToOne manuel):

class BaseExtendedUser(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")

    class Meta:
        abstract = True

class Administrator(BaseExtendedUser):
    province = models.ForeignKey(Province)

class Principal(BaseExtendedUser):
    branchoffice = models.ForeignKey(BranchOffice)

Je ne pense pas que je hériter du modèle User plutôt utiliser un UserProfile personnalisé - en laissant le modèle contrib.auth seul. Avec le modèle UserProfile personnalisé, vous pouvez configurer un Base modèle de profil de l'utilisateur qui peut être une partie de tous vos différents types d'utilisateurs.

Il suffit de regarder rapidement, aussi, je regardais attentivement tous les modèles qui se répètent tous les mêmes champs (comme vos deux derniers modèles de Principle et Administrator). En combinant les fonctionnalités intégrées de groupe avec l'idée de profil utilisateur peut faire ce que vous recherchez.

S'il vous plaît considérer ce qui se passe dans le modèle de données lorsqu'un coordonnateur est promu à un principal. Je ne voudrais pas utiliser l'héritage dans ce cas. S'il vous plaît revenir sur la suggestion de l'affiche précédente « La combinaison de la fonctionnalité intégrée de groupe avec l'idée de profil utilisateur peut faire ce que vous recherchez. »

Avez-vous besoin d'objets de vos classes d'utilisateurs d'agir comme un auth.User partout? Ce serait la raison la plus évidente d'utiliser l'héritage sur OneToOne. Un pro de l'approche OneToOne serait la facilité avec laquelle vous pouvez passer à un autre modèle de l'utilisateur, si c'est une préoccupation.

La vraie question que je vois ce que vous avez ci-dessus (soit par la méthode) est qu'il ne semble pas être quelque chose qui vous empêche d'avoir un objet principal et un partage d'objets Administrateur même utilisateur. OneToOneField ne peut garantir un à un entre les deux relations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top