models.py ficando enorme, qual é a melhor maneira de separar?

91

Instruções do meu supervisor: "Quero evitar colocar qualquer lógica no models.py. De agora em diante, vamos usar isso apenas como classes para acessar o banco de dados e manter toda a lógica nas classes externas que usam as classes de modelos, ou agrupá-las."

Eu sinto que esse é o caminho errado a seguir. Eu sinto que manter a lógica fora dos modelos apenas para manter o arquivo pequeno é uma má ideia. Se a lógica for melhor no modelo, é aí que ela realmente deve ir, independentemente do tamanho do arquivo.

Portanto, há uma maneira simples de usar apenas os includes? Em linguagem PHP, gostaria de propor ao supervisor que acabamos demodels.py incluíssemos () as classes de modelo de outros lugares. Conceitualmente, isso permitiria que os modelos tivessem toda a lógica que desejamos, mas manteria o tamanho do arquivo baixo aumentando o número de arquivos (o que leva a menos problemas de controle de revisão, como conflitos, etc.).

Então, existe uma maneira simples de remover classes de modelo do arquivo models.py, mas ainda fazer os modelos funcionarem com todas as ferramentas Django? Ou existe uma solução completamente diferente, mas elegante, para o problema geral de um arquivo models.py "grande"? Qualquer entrada seria apreciada.

Edificado
fonte
7
Você conhece a declaração de importação, certo?
balpha
7
PS. Não quero dizer ofensivamente, só quero saber onde você está.
balpha
1
Sim, mas eu não sabia se as ferramentas de administração do django funcionariam apenas usando declarações de importação para puxar os modelos. Prefiro perguntar aqui do que gastar muito tempo tentando usar as importações ole simples apenas para descobrir que as ferramentas do Django não funcionam bem com elas. Admito que sou mais novo em python e django, então provavelmente estou apenas
começando a

Respostas:

64

Django é projetado para permitir que você construa muitos pequenos aplicativos em vez de um grande aplicativo.

Dentro de cada aplicativo grande existem muitos aplicativos pequenos lutando para ser gratuitos.

Se você se models.pysente grande, você está fazendo muito. Pare. relaxar. Decompor.

Encontre pequenos componentes ou peças de aplicativo menores e potencialmente reutilizáveis. Você não tem que realmente reutilizá-los. Pense neles como potencialmente reutilizáveis.

Considere seus caminhos de atualização e decomponha os aplicativos que você pode querer substituir algum dia. Você não precisa realmente substituí-los, mas pode considerá-los como um "módulo" independente de programação que pode ser substituído por algo mais legal no futuro.

Temos cerca de uma dúzia de aplicativos, cada model.pyum com não mais do que cerca de 400 linhas de código. Eles estão todos muito focados em menos de meia dúzia de definições de classes discretas. (Esses não são limites rígidos, são observações sobre nosso código.)

Nós nos decompomos cedo e frequentemente.

S.Lott
fonte
1
bem no ponto. qualquer webapp não trivial seria vários pequenos 'aplicativos'. dê uma dica do contrib e de outros aplicativos populares, autenticação de usuário é um aplicativo, marcação é outro, perfis de usuário mais um, etc.
Javier
4
Embora essa seja a maneira "certa" e seja útil saber, não é bem o que eu estava procurando. Peço desculpas se não houve como saber que tipo de resposta eu estava procurando. :)
editado em
@Eddified: se você não fizer isso, só vai piorar. Comece a dividir agora.
S.Lott
Engraçado, neste exato momento estou ouvindo Jacob Kaplan Moss (da OSCON) explicando exatamente isso em detalhes grandes e fortemente justificados ;-).
Alex Martelli
13
A resposta de Glenn Maynard é muito melhor neste caso. Dividir um webapp complexo em muitos aplicativos é certamente uma boa prática, mas refatorar um arquivo model.py DENTRO de um aplicativo também é. As duas ações podem ser ortogonais.
Erik
108

É natural que as classes de modelo contenham métodos para operar no modelo. Se eu tiver um modelo de Livro, com um método book.get_noun_count(), é onde ele pertence - não quero ter que escrever " get_noun_count(book)", a menos que o método realmente pertença intrinsecamente a algum outro pacote. (Pode - por exemplo, se eu tiver um pacote para acessar a API da Amazon com "get_amazon_product_id(book) ".)

Eu me encolhi quando a documentação do Django sugeriu colocar os modelos em um único arquivo, e levei alguns minutos desde o início para descobrir como dividi-lo em um subpacote adequado.

site/models/__init__.py
site/models/book.py

__init__.py parece:

from .book import Book

então eu ainda posso escrever "from site.models import Book".


O seguinte é necessário apenas para versões anteriores ao Django 1.7, consulte https://code.djangoproject.com/ticket/3591

O único truque é que você precisa definir explicitamente o aplicativo de cada modelo, devido a um bug no Django: ele assume que o nome do aplicativo é a terceira à última entrada no caminho do modelo. "site.models.Book" resulta em "site", o que é correto; "site.models.book.Book" faz pensar que o nome do aplicativo é "modelos". Este é um hack muito desagradável da parte de Django; ele provavelmente deve pesquisar a lista de aplicativos instalados por uma correspondência de prefixo.

class Book(models.Model):
    class Meta: app_label = "site"

Você provavelmente poderia usar uma classe base ou metaclasse para generalizar isso, mas ainda não me preocupei com isso.

Glenn Maynard
fonte
2
1 Usei isso com sucesso. Embora S. Lott esteja certo em vários aplicativos sendo uma boa ideia, esta é a solução aqui e agora.
Alexander Ljungberg
35
Não vejo muitos benefícios em dividir as coisas em um monte de aplicativos, quando seus modelos estão intimamente e intrinsecamente relacionados.
Glenn Maynard
2
Isso me interessa. Eu li o scompt do link wiki django postado e achei isso: "Foi verificado que funciona sem a classe Meta app_labels, no branch principal atual." Então, isso significa que se você estiver trabalhando com o branch principal, podemos descartar as coisas Meta: app_label? É confuso pois fica depois do comentário sobre o ticket para resolver esse problema.
Dan.StackOverflow
2
Acabei de testar com o tronco (a partir de hoje, r11286); se o app_name não estiver definido, o modelo simplesmente não aparece em "sqlall appname" e provavelmente não será criado pelo syncdb (mas eu não uso isso, então não posso testá-lo). É um caso de erro bastante confuso, porque não aciona nenhum erro; apenas silenciosamente não aparece.
Glenn Maynard
2
Uau, quase 10 anos depois e ainda amo essa solução. Concordou que é uma abordagem muito melhor do que dividir seu código em aplicativos menores, o que, em minha opinião, pode levar a uma base de código que é difícil de raciocinar.
Michael Hays
5

Não consigo entender qual dos muitos problemas possíveis você pode ter. Aqui estão algumas possibilidades com respostas:

  • vários modelos no mesmo arquivo

    Coloque-os em arquivos separados. Se houver dependências, use import para obter os modelos adicionais.

  • funções lógicas / utilitárias estranhas em models.py

    Coloque a lógica extra em arquivos separados.

  • métodos estáticos para selecionar algumas instâncias de modelo do banco de dados

    Crie um novo gerente em um arquivo separado.

  • métodos obviamente relacionados ao modelo

    save, __unicode__ e get_absolute_url são exemplos.

Hughdbrown
fonte