Divida models.py em vários arquivos

91

Estou tentando dividir o models.pydo meu aplicativo em vários arquivos:

Meu primeiro palpite foi fazer isto:

myproject/
    settings.py
    manage.py
    urls.py
    __init__.py
    app1/
        views.py
        __init__.py
        models/
            __init__.py
            model1.py
            model2.py
    app2/
        views.py
        __init__.py
        models/
            __init__.py
            model3.py
            model4.py

Isso não funciona, então eu achei isso , mas nesta solução ainda tenho um problema, quando eu executo python manage.py sqlall app1eu tenho algo como:

BEGIN;
CREATE TABLE "product_product" (
    "id" serial NOT NULL PRIMARY KEY,
    "store_id" integer NOT NULL
)
;
-- The following references should be added but depend on non-existent tables:
-- ALTER TABLE "product_product" ADD CONSTRAINT "store_id_refs_id_3e117eef" FOREIGN KEY     ("store_id") REFERENCES "store_store" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "product_product_store_id" ON "product_product" ("store_id");
COMMIT;

Não tenho certeza sobre isso, mas estou preocupado com a parte The following references should be added but depend on non-existent tables:

Este é meu arquivo model1.py:

from django.db import models

class Store(models.Model):
    class Meta:
        app_label = "store"

Este é meu arquivo model3.py:

from django.db import models

from store.models import Store

class Product(models.Model):
    store = models.ForeignKey(Store)
    class Meta:
        app_label = "product"

E aparentemente funciona, mas eu tenho o comentário alter tablee se eu tentar isso, acontece a mesma coisa:

class Product(models.Model):
    store = models.ForeignKey('store.Store')
    class Meta:
        app_label = "product"

Portanto, devo executar o alter para referências manualmente? isso pode me trazer problemas com o sul?

diegueus9
fonte
O que acontece no modelo 3 se você tentar from app1.models.model1 import Store?
James Khoury de
Além disso, você pode querer verificar stackoverflow.com/questions/5534206/…
James Khoury

Respostas:

33

Eu faria o seguinte:

myproject/
    ...
    app1/
        views.py
        __init__.py
        models.py
        submodels/
            __init__.py
            model1.py
            model2.py
    app2/
        views.py
        __init__.py
        models.py
        submodels/
            __init__.py
            model3.py
            model4.py

Então

#myproject/app1/models.py:
    from submodels/model1.py import *
    from submodels/model2.py import *

#myproject/app2/models.py:
    from submodels/model3.py import *
    from submodels/model4.py import *

Mas, se você não tiver um bom motivo, coloque model1 e model2 diretamente em app1 / models.py e model3 e model4 em app2 / models.py

---segunda parte---

Este é o arquivo app1 / submodels / model1.py:

from django.db import models
class Store(models.Model):
    class Meta:
        app_label = "store"

Portanto, corrija seu arquivo model3.py:

from django.db import models
from app1.models import Store

class Product(models.Model):
    store = models.ForeignKey(Store)
    class Meta:
        app_label = "product"

Editado, caso isso aconteça novamente para alguém: Verifique django-schedule para ver um exemplo de projeto que faz exatamente isso. https://github.com/thauber/django-schedule/tree/master/schedule/models https://github.com/thauber/django-schedule/

Ted
fonte
1
Com relação a esta resposta eu tenho outro problema quando em models2.py faço algo como from product.models import Product: ImportError: Nenhum módulo chamado models
diegueus9
68
você faria isso dessa maneira para manter uma classe por arquivo.
worc
50
O "porquê" seria o desejo de reduzir o tamanho de um models.pyarquivo enorme . Recentemente fiz isso quando o meu cresceu para mais de 15 mil linhas de código. Grande redação embora. O processo é bastante simples. A principal advertência é que você deve se lembrar de definir um app_label explícito, já que o Django extrai isso do módulo imediato por padrão.
Cerin
1
Experimentando em 2016. A segunda parte deste post ainda é necessária? Eu apenas movi as classes para arquivos separados, escrevi o meu __init__.pye tudo parece estar funcionando bem. Não precisei corrigir meus arquivos de modelo. Sou capaz de buscar e criar objetos do shell e do administrador django. Estou tentando django há uma semana, então estou me perguntando se a versão mais recente agora permite que isso aconteça.
Vic
3
@Vic: O app_label na classe Meta não é mais necessário com as versões mais recentes do Django. Consulte code.djangoproject.com/wiki/CookBookSplitModelsToFiles
jrial
149

Para qualquer pessoa no Django 1.9, agora é suportado pelo framework sem definir os metadados da classe.

https://docs.djangoproject.com/en/1.9/topics/db/models/#organizing-models-in-a-package

NOTA: Para Django 2, ainda é o mesmo

O manage.py startappcomando cria uma estrutura de aplicativo que inclui um arquivo models.py. Se você tiver muitos modelos, organizá-los em arquivos separados pode ser útil.

Para fazer isso, crie um pacote de modelos. Remova models.py e crie ummyapp/models/ diretório com um __init__.pyarquivo e os arquivos para armazenar seus modelos. Você deve importar os modelos no __init__.pyarquivo.

Então, no seu caso, para uma estrutura como

app1/
    views.py
    __init__.py
    models/
        __init__.py
        model1.py
        model2.py
app2/
    views.py
    __init__.py
    models/
        __init__.py
        model3.py
        model4.py

Você só precisa fazer

#myproject/app1/models/__init__.py:
from .model1 import Model1
from .model2 import Model2

#myproject/app2/models/__init__.py:
from .model3 import Model3
from .model4 import Model4

Uma observação contra a importação de todas as classes:

Importar explicitamente cada modelo em vez de usar from .models import *tem as vantagens de não sobrecarregar o namespace, tornando o código mais legível e mantendo as ferramentas de análise de código úteis.

Vic
fonte
6
Caso alguém esteja se perguntando se ele ainda está atualizado com o Django 2.0 docs.djangoproject.com/en/2.0/topics/db/models/…
NaturalBornCamper
7
NOTA: Isso geralmente não funciona corretamente quando você inicia models.pye deseja migrar mais tarde. Nesse caso, você deve excluir suas migrações e seu banco de dados: / Ou resolver manualmente todos os erros em todos os arquivos de migração
tuergeist
Melhor resposta ainda. Pode confirmar que ainda funciona em 2.1+
Roys
1
Não vejo o problema apontado por @tuergeist no Django 3.0. Parece funcionar como um encanto
caram
11

Na verdade, encontrei um tutorial para exatamente o que você está perguntando, você pode visualizá-lo aqui:

http://paltman.com/breaking-apart-models-in-django/

Um ponto-chave que provavelmente é relevante - você pode querer usar o campo db_table na classe Meta para apontar as classes realocadas de volta para sua própria tabela.

Posso confirmar que essa abordagem está funcionando no Django 1.3

Adam Luchjenbroers
fonte
Esse link dá um 404
Bryan Oakley
1

Passos mais fáceis:

  1. Crie uma pasta de modelo em seu aplicativo (o nome da pasta deve ser o modelo )
  2. Exclua o arquivo model.py do diretório do aplicativo (faça backup do arquivo enquanto o exclui)
  3. E depois de criar o init arquivo .py na pasta do modelo
  4. E após o arquivo init .py em escrever uma linha simples
  5. E depois de criar o arquivo de modelo em sua pasta de modelo e o nome do arquivo de modelo deve ser igual ao nome da classe, se o nome da classe for 'Funcionário', então o nome do arquivo de modelo deve ser como 'funcionário.py'
  6. E depois no arquivo de modelo, defina sua tabela de banco de dados mesmo que escreva no arquivo model.py
  7. Salve isso

Meu código: from django_adminlte.models.employee import Employee

Para o seu: de APP_NAME .models. model_file_name_only import Class_Name_which_define_in_model_file


__init__.py

from django_adminlte.models.employee import Employee

model/employee.py (employee is separate model file)

from django.db import models

class Employee(models.Model):
eid = models.CharField(max_length=20)
ename = models.CharField(max_length=20)
eemail = models.EmailField()
econtact = models.CharField(max_length=15)

class Meta:
    db_table = "employee"
    # app_label = 'django_adminlte'
    
def __str__(self):
    return self.ename
Parth Jani
fonte
2
Isso é exatamente o que ele está tentando consertar. Esta solução causará RuntimeError ModelX doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.no django 2.x.
radtek
0

Eu escrevi um script que pode ser útil.

github.com/victorqribeiro/splitDjangoModels

divide os modelos em arquivos individuais com nomenclatura e importação adequadas; ele também cria um arquivo init para que você possa importar todos os seus modelos de uma vez.

avise-me se isso ajudar

user1659565
fonte