Split models.py into several files
Asked Answered
V

6

133

I'm trying to split the models.py of my app into several files:

My first guess was do this:

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

This doesn't work, then i found this, but in this solution i still have a problem, when i run python manage.py sqlall app1 I got something like:

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;

I'm not pretty sure about this, but i'm worried aboout the part The following references should be added but depend on non-existent tables:

This is my model1.py file:

from django.db import models

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

This is my model3.py file:

from django.db import models

from store.models import Store

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

And apparently works but i got the comment in alter table and if I try this, same thing happens:

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

So, should I run the alter for references manually? this may bring me problems with south?

Vanquish answered 13/6, 2011 at 21:34 Comment(2)
What happens in model 3 if you try from app1.models.model1 import Store?Knavish
Also you might want to check out #5534706Knavish
F
42

I'd do the following:

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

Then

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

But, if you don't have a good reason, put model1 and model2 directly in app1/models.py and model3 and model4 in app2/models.py

---second part---

This is app1/submodels/model1.py file:

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

Thus correct your model3.py file:

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

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

Edited, in case this comes up again for someone: Check out django-schedule for an example of a project that does just this. https://github.com/thauber/django-schedule/tree/master/schedule/models https://github.com/thauber/django-schedule/

Filigree answered 14/6, 2011 at 3:18 Comment(9)
With respect to this answer i got another problem when in models2.py make something like from product.models import Product: ImportError: No module named modelsVanquish
from appx.models import ProductFiligree
you would do it this way to maintain one class per file.Frecklefaced
The "why" would be the desire to reduce the size of a massive models.py file. I recently did this when mine grew to over 15k lines of code. Great writeup though. The process is fairly simple. The main caveat being you have to remember to define an explicit app_label, since Django extracts this from the immediate module by default.Planetesimal
Giving this a try in 2016. Is the second part of this post still needed? I only moved the classes to separate files, wrote my __init__.py and everything seems to be working okay. I did not have to correct my model files. I'm able to fetch and create objects from the shell and the django admin. I've been trying django for like a week so I'm wondering if the newest version now allows for this to happen?Lightyear
@Vic: The app_label in the Meta class is no longer necessary with newer versions of Django. See code.djangoproject.com/wiki/CookBookSplitModelsToFilesPlectrum
I still get "No changes detected" when I run makemigrationsNiobic
Is there a way to configure the init.py file to import every class in the models/ folder? Having to remember to add an import to the init when creating a new class can be a nuisance when in a team and not all are familiar with Django.Rubierubiginous
what if I wanted to put all models in one folder just under the myproject/ what should I do about that ?Rudiger
L
196

For anyone on Django 1.9, it is now supported by the framework without defining the class meta data.

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

NOTE: For Django 2, it's still the same

The manage.py startapp command creates an application structure that includes a models.py file. If you have many models, organizing them in separate files may be useful.

To do so, create a models package. Remove models.py and create a myapp/models/ directory with an __init__.py file and the files to store your models. You must import the models in the __init__.py file.

So, in your case, for a structure like

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

You only need to do

#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

A note against importing all the classes:

Explicitly importing each model rather than using from .models import * has the advantages of not cluttering the namespace, making code more readable, and keeping code analysis tools useful.

Lightyear answered 11/6, 2016 at 0:11 Comment(6)
In case someone is wondering if it's still up to date with Django 2.0 docs.djangoproject.com/en/2.0/topics/db/models/…Administrative
NOTE: This usually does not work properly when you have started with models.py and want to migrate later. In that case, you have to delete your migrations and your database :/ Or to manually solve all errors in all migration filesUnderlaid
Best answer still. Can confirm this still work in 2.1+Gait
I don't see the problem pointed out by @Underlaid in Django 3.0. Seems to work like a charmWalz
I had a problem running migrations in python 3.2. this answer seems to be still up to date because documentation is still the same regarding this: docs.djangoproject.com/en/3.2/topics/db/modelsBoehmenism
If you run into a problem with migrations as mentioned in the comments, this may be because the module import structure for some references present in migration files, which can happen when refactoring a large models file. It should be safe to update those references as needed to match the post-refactoring module structure.Allanallana
F
42

I'd do the following:

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

Then

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

But, if you don't have a good reason, put model1 and model2 directly in app1/models.py and model3 and model4 in app2/models.py

---second part---

This is app1/submodels/model1.py file:

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

Thus correct your model3.py file:

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

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

Edited, in case this comes up again for someone: Check out django-schedule for an example of a project that does just this. https://github.com/thauber/django-schedule/tree/master/schedule/models https://github.com/thauber/django-schedule/

Filigree answered 14/6, 2011 at 3:18 Comment(9)
With respect to this answer i got another problem when in models2.py make something like from product.models import Product: ImportError: No module named modelsVanquish
from appx.models import ProductFiligree
you would do it this way to maintain one class per file.Frecklefaced
The "why" would be the desire to reduce the size of a massive models.py file. I recently did this when mine grew to over 15k lines of code. Great writeup though. The process is fairly simple. The main caveat being you have to remember to define an explicit app_label, since Django extracts this from the immediate module by default.Planetesimal
Giving this a try in 2016. Is the second part of this post still needed? I only moved the classes to separate files, wrote my __init__.py and everything seems to be working okay. I did not have to correct my model files. I'm able to fetch and create objects from the shell and the django admin. I've been trying django for like a week so I'm wondering if the newest version now allows for this to happen?Lightyear
@Vic: The app_label in the Meta class is no longer necessary with newer versions of Django. See code.djangoproject.com/wiki/CookBookSplitModelsToFilesPlectrum
I still get "No changes detected" when I run makemigrationsNiobic
Is there a way to configure the init.py file to import every class in the models/ folder? Having to remember to add an import to the init when creating a new class can be a nuisance when in a team and not all are familiar with Django.Rubierubiginous
what if I wanted to put all models in one folder just under the myproject/ what should I do about that ?Rudiger
T
13

I've actually come across a tutorial for exactly what you're asking about, you can view it here:

https://web.archive.org/web/20190331105757/http://paltman.com/breaking-apart-models-in-django/

One key point that's probably relevant - you may want to use the db_table field on the Meta class to point the relocated classes back at their own table.

I can confirm this approach is working in Django 1.3

Togs answered 31/7, 2011 at 13:55 Comment(2)
Unfortunately that link seems to no longer work :(Senior
Looks like Wayback Machine still has it: web.archive.org/web/20190331105757/http://paltman.com/…Togs
D
10

The relevant link for Django 3 is:

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

Links to previous versions of documentation are broken. The example there is very succinct:

To do so, create a models package. Remove models.py and create a myapp/models/ directory with an init.py file and the files to store your models. You must import the models in the init.py file.

For example, if you had organic.py and synthetic.py in the models directory:

from .organic import Person
from .synthetic import Robot
Dipole answered 29/6, 2021 at 13:12 Comment(0)
M
2

Easiest Steps :

  1. Create model folder in your app (Folder name should be model)
  2. Delete model.py file from app directory (Backup the file while you delete it)
  3. And after create init.py file in model folder
  4. And after init.py file in write simple one line
  5. And after create model file in your model folder and model file name should be same like as class name,if class name is 'Employee' than model file name should be like 'employee.py'
  6. And after in model file define your database table same as write like in model.py file
  7. Save it

My Code : from django_adminlte.models.employee import Employee

For your : from 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
Migdaliamigeon answered 2/2, 2019 at 10:4 Comment(1)
This is exactly what he is trying to fix. This solution will cause RuntimeError ModelX doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS. in django 2.x.Secor
E
0

I wrote a script that might be useful.

github.com/victorqribeiro/splitDjangoModels

it split the models in individual files with proper naming and importing; it also create an init file so you can import all your models at once.

let me know if this helps

Ebbie answered 13/6, 2011 at 21:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.