Podziel models.py na kilka plików

89

Próbuję podzielić models.pymoją aplikację na kilka plików:

Moje pierwsze przypuszczenie było takie:

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

To nie działa, to znalazłem to , ale w tym rozwiązaniu nadal mam problem, po uruchomieniu pojawia python manage.py sqlall app1się coś takiego:

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;

Nie jestem tego całkiem pewien, ale martwię się o tę część The following references should be added but depend on non-existent tables:

To jest mój plik model1.py:

from django.db import models

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

To jest mój plik 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"

I najwyraźniej działa, ale dostałem komentarz alter tablei jeśli spróbuję, stanie się to samo:

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

Czy powinienem więc ręcznie uruchomić zmianę dla odwołań? to może przynieść mi problemy z południem?

diegueus9
źródło
Co się stanie w modelu 3, jeśli spróbujesz from app1.models.model1 import Store?
James Khoury,
Możesz także sprawdzić stackoverflow.com/questions/5534206/…
James Khoury,

Odpowiedzi:

32

Zrobiłbym co następuje:

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

Następnie

#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 *

Ale jeśli nie masz dobrego powodu, umieść model1 i model2 bezpośrednio w app1 / models.py oraz model3 i model4 w app2 / models.py

---druga część---

To jest plik app1 / submodels / model1.py:

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

Dlatego popraw plik 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"

Edytowane, na wypadek, gdyby ktoś znowu się pojawił: Sprawdź django-schedule, aby zobaczyć przykład projektu, który właśnie to robi. https://github.com/thauber/django-schedule/tree/master/schedule/models https://github.com/thauber/django-schedule/

Przetrząsać
źródło
1
W odniesieniu do tej odpowiedzi mam inny problem, gdy w model2.py zrobię coś takiego from product.models import Product: ImportError: Brak modułu o nazwie models
diegueus9
68
zrobiłbyś to w ten sposób, aby utrzymać jedną klasę na plik.
worc
50
„Dlaczego” to chęć zmniejszenia rozmiaru ogromnego models.pypliku. Niedawno zrobiłem to, gdy mój urósł do ponad 15 tysięcy linii kodu. Świetny opis. Proces jest dość prosty. Głównym zastrzeżeniem jest to, że musisz pamiętać o zdefiniowaniu jawnej etykiety app_label, ponieważ Django domyślnie wyodrębnia ją z modułu bezpośredniego.
Cerin
1
Spróbuję w 2016 roku. Czy druga część tego postu jest nadal potrzebna? Przeniosłem tylko klasy do oddzielnych plików, napisałem moje __init__.pyi wszystko wydaje się działać dobrze. Nie musiałem poprawiać plików modelu. Jestem w stanie pobierać i tworzyć obiekty z powłoki i administratora django. Próbuję django od tygodnia, więc zastanawiam się, czy najnowsza wersja pozwala teraz na to?
Vic,
3
@Vic: etykieta app_label w klasie Meta nie jest już potrzebna w nowszych wersjach Django. Zobacz code.djangoproject.com/wiki/CookBookSplitModelsToFiles
jrial
147

Dla każdego, kto używa Django 1.9, jest teraz obsługiwany przez framework bez definiowania metadanych klasy.

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

UWAGA: W przypadku Django 2 jest nadal tak samo

manage.py startappPolecenie tworzy strukturę aplikacji, która zawiera plik models.py. Jeśli masz wiele modeli, przydatne może być zorganizowanie ich w osobnych plikach.

Aby to zrobić, utwórz pakiet modeli. Usuń models.py i utwórz myapp/models/katalog z __init__.pyplikiem i plikami do przechowywania modeli. Musisz zaimportować modele w __init__.pypliku.

Tak więc w twoim przypadku dla struktury takiej jak

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

Musisz tylko zrobić

#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

Uwaga przeciwko importowaniu wszystkich klas:

Jawne importowanie każdego modelu zamiast używania from .models import *ma zalety polegające na tym, że nie zaśmieca przestrzeni nazw, dzięki czemu kod jest bardziej czytelny i narzędzia do analizy kodu są przydatne.

Vic
źródło
5
Na wypadek, gdyby ktoś się zastanawiał, czy nadal jest aktualny z Django 2.0 docs.djangoproject.com/en/2.0/topics/db/models/ ...
NaturalBornCamper
6
UWAGA: To zwykle nie działa poprawnie, gdy zacząłeś models.pyi chcesz przeprowadzić migrację później. W takim przypadku musisz usunąć swoje migracje i bazę danych: / Lub ręcznie rozwiązać wszystkie błędy we wszystkich plikach migracji
tuergeist
Najlepsza odpowiedź wciąż. Potwierdzam, że to nadal działa w wersji 2.1+
Roys,
Nie widzę problemu wskazanego przez @tuergeist w Django 3.0. Wydaje się działać jak urok
caram
11

Właściwie trafiłem na samouczek dotyczący dokładnie tego, o co pytasz, możesz go zobaczyć tutaj:

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

Jedna kluczowa kwestia, która prawdopodobnie ma znaczenie - możesz chcieć użyć pola db_table w klasie Meta, aby skierować przeniesione klasy z powrotem do ich własnej tabeli.

Mogę potwierdzić, że to podejście działa w Django 1.3

Adam Luchjenbroers
źródło
Ten link daje wynik 404
Bryan Oakley,
1

Najłatwiejsze kroki:

  1. Utwórz folder modelu w swojej aplikacji (nazwa folderu powinna być modelem )
  2. Usuń plik model.py z katalogu aplikacji ( wykonaj kopię zapasową pliku podczas usuwania)
  3. A po utworzeniu pliku init .py w folderze modelu
  4. A po inicjalizacji pliku .py w prostej linii
  5. Po utworzeniu pliku modelu w folderze modelu, a nazwa pliku modelu powinna być taka sama jak nazwa klasy, jeśli nazwa klasy to „Pracownik”, wówczas nazwa pliku modelu powinna wyglądać jak „pracownik.py”
  6. Następnie w pliku modelu zdefiniuj tabelę bazy danych tak samo, jak w pliku model.py
  7. Zapisz to

Mój kod: z django_adminlte.models.employee importuj pracownika

Dla twojego: z 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
źródło
2
To jest dokładnie to, co próbuje naprawić. To rozwiązanie spowoduje, że RuntimeError ModelX doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.w django 2.x.
radtek
0

Napisałem skrypt, który może się przydać.

github.com/victorqribeiro/splitDjangoModels

dzieli modele na poszczególne pliki z odpowiednim nazewnictwem i importowaniem; tworzy również plik init, dzięki czemu można importować wszystkie modele naraz.

daj mi znać, jeśli to pomoże

user1659565
źródło