Django Multi-Table Herança vs Especificando o relacionamento explícito de OnetoOne em modelos

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

  •  22-09-2019
  •  | 
  •  

Pergunta

Espero que tudo isso faça sentido :) Vou esclarecer por meio de comentários, se necessário. Além disso, estou experimentando usando o texto em negrito nesta pergunta e editá -lo se eu (ou você) achar que é distraído. Com isso fora do caminho ...

O uso de django.contrib.auth nos dá usuário e grupo, entre outras coisas úteis que eu não posso fazer sem (como mensagens básicas).

No meu aplicativo, tenho vários tipos diferentes de usuários. Um usuário pode ter apenas um tipo. Isso seria facilmente tratado por grupos, com um pouco de cuidado extra. No entanto, esses diferentes usuários estão relacionados entre si em hierarquias / relacionamentos.

Vamos dar uma olhada nesses usuários: -

Principais - usuários de "nível superior"

Administradores - Cada administrador se reporta a um principal

Coordenadores - Cada coordenador se reporta a um administrador

Além disso Existem outros tipos de usuário que não estão diretamente relacionados, mas pode ser relacionado mais tarde. Por exemplo, "Empresa" é outro tipo de usuário e pode ter vários "produtos", e os produtos podem ser supervisionados por um "coordenador". "Comprador" é outro tipo de usuário que pode comprar produtos.

Agora tudo isso Os usuários têm vários outros atributos, alguns dos quais são comuns a todos os tipos de usuários e alguns dos quais são distintos apenas para um tipo de usuário. Por exemplo, todos os tipos de usuários precisam ter um endereço. Por outro lado, apenas o usuário principal pertence a um "filialffice".

Outro ponto, afirmado acima, é que Um usuário só pode ser do tipo.

O aplicativo também precisa acompanhar quem criou e/ou diretores, administradores, coordenadores, empresas, produtos etc.. (Então, são mais dois links para o modelo de usuário.)

Nesse cenário, é uma boa ideia usar a herança de várias peças da Django da seguinte maneira: -

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 devo fazer assim: -

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

Lembre -se de que existem outros tipos de usuário relacionados por chaves estrangeiras, por exemplo: -

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

Estou ciente de que o Django usa um relacionamento individual para herança de várias mesa nos bastidores. Só não sou qualificado o suficiente para decidir qual é uma abordagem mais sólida.

Foi útil?

Solução

Eu gostaria de expandir a solução por @thornomad.

Estender a classe de usuário do Django diretamente pode causar todos os tipos de problemas com os mecanismos internos do django.auth. O que eu fiz em uma situação semelhante é exatamente o que o @Thornomad sugere-fiz meu próprio modelo de uso do usuário vinculado um para um ao modelo de usuário do Django, no qual mantive dados adicionais do usuário e dos quais herdei modelos para diferentes tipos de usuários.

Algo para se encaixar no que você descreveu:

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)

Outras dicas

Recentemente, mudei para o uso de modelos que herdam do contrib.auto.models.user. Minha observação geral é que, em teoria, eles são ótimos, mas às vezes eles não são tratados automaticamente como deveriam.

Eu acho que sua decisão sobre herança vs. onetoone se resume a isso:

  • Eu quero ter Django automaticamente fazer algo certo 95% das vezes e preciso depurar esses outros 5%

-OU-

  • Eu quero fazer algo manualmente eu mesmo 100% do tempo

Se você não viu, o blog de Scott Barham tem um ótimo post sobre herdar o usuário e também construir um back -end personalizado para garantir que seu objeto personalizado esteja sendo devolvido - Estendendo o usuário do django.

Além disso, de interesse seria o campo AutoOnetoOne fornecido por Django-anying. É uma espécie de híbrido das duas abordagens aqui - não há herança ocorrendo, mas o Django está cuidando de criar o OnetoOnefield correspondente se não estiver presente.

Também, Thornomad faz um bom sentido sobre a redundância em seus modelos. Você pode facilmente implementar uma classe abstrata para limpar isso como assim (supondo que você esteja fazendo onetoone manual):

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)

Eu não acho que herdaria o User modelo, em vez de usar um personalizado UserProfile - Deixe o contrib.auth Modelo sozinho. Com o costume UserProfile modelo, você pode configurar um base Modelo de perfil de usuário que pode fazer parte de todos os seus diferentes tipos de usuário.

Apenas olhando para isso rapidamente, eu também examinava cuidadosamente os modelos que repetem todos os mesmos campos (como seus últimos dois Principle e Administrator modelos). Combinar a funcionalidade de grupo embutida com a ideia do perfil do usuário pode fazer o que você está procurando.

Por favor, considere o que acontece no modelo de dados quando um coordenador for promovido a um diretor. Eu não usaria a herança neste caso. Por favor, reconsidere a sugestão do pôster anterior "Combinando a funcionalidade de grupo embutida com a ideia do perfil do usuário pode fazer o que você está procurando".

Você precisa de objetos de suas classes de usuário para agir como um auth.User em qualquer lugar? Essa seria a razão mais óbvia para usar a herança sobre o OnetoOne. Um profissional da abordagem OnetoOne seria a facilidade em que você pode mudar para outro modelo de usuário, se isso for uma preocupação.

O problema real que vejo com o que você tem acima (por qualquer método) é que não parece haver nada que o impeça de ter um objeto principal e um objeto de administrador compartilhe o mesmo usuário. O OnetoOnefield só pode garantir um mapeamento individual entre duas relações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top