将models.py拆分为几个文件

131
我试图将我的应用程序的models.py文件拆分成几个文件:

我最初的想法是这样做:

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

这个方法不起作用,然后我找到了这个,但是在这个解决方案中,我仍然遇到了一个问题,当我运行 python manage.py sqlall app1时,我得到了类似下面的输出:

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;

我不是很确定,但我担心这一部分:The following references should be added but depend on non-existent tables:

这是我的 model1.py 文件:

from django.db import models

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

这是我的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"

看起来它可以工作,但是我在alter table中得到了评论,如果我尝试这样做,同样的事情会发生:

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

那我是不是应该手动运行alter for references? 这样可能会给我在south方面带来麻烦吗?


如果您尝试使用 from app1.models.model1 import Store,会发生什么事情呢?在模型3中会发生什么? - James Khoury
同时,您可能想要查看 https://dev59.com/D2035IYBdhLWcg3wPdkS#5534251。 - James Khoury
6个回答

194

如果你使用的是Django 1.9版本,现在框架已经支持不需要定义类元数据。

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

注意:对于Django 2,仍然是一样的

manage.py startapp命令会创建一个包含models.py文件的应用程序结构。如果你有很多模型,将它们组织到不同的文件中可能很有用。

为此,请创建一个models包。删除models.py并创建一个myapp/models/目录,其中包含__init__.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

你只需要做

#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

关于导入所有类的说明:

明确导入每个模型,而不是使用from .models import *有以下优点:不会使命名空间混乱,使代码更易读,并保持代码分析工具的有用性。


7
如果有人想知道它是否与Django 2.0仍然保持最新状态 https://docs.djangoproject.com/en/2.0/topics/db/models/#organizing-models-in-a-package - NaturalBornCamper
11
注意:如果你已经从models.py开始,稍后想要进行迁移通常会出现问题。在这种情况下,你必须删除所有迁移和数据库 :/ 或手动解决所有迁移文件中的错误。 - tuergeist
1
最佳答案仍然有效。可以确认这在2.1+版本上仍然有效。 - Royce
6
我没有看到@tuergeist在Django 3.0中指出的问题。它似乎运行得很好。 - caram
我在Python 3.2中运行迁移时遇到了问题。这个答案似乎仍然是最新的,因为文档仍然与此相关:https://docs.djangoproject.com/en/3.2/topics/db/models/ - Alvaro Rodriguez Scelza
如果你在迁移过程中遇到了评论中提到的问题,可能是因为迁移文件中存在一些引用的模块导入结构,这可能发生在重构大型模型文件时。根据需要更新这些引用以匹配重构后的模块结构应该是安全的。 - undefined

40

我会按照以下步骤进行:

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

那么

#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 *
但是,如果没有充分的理由,请将model1和model2直接放在app1/models.py中,将model3和model4放在app2/models.py中。
这是app1/submodels/model1.py文件:
from django.db import models
class Store(models.Model):
    class Meta:
        app_label = "store"

因此,请修正您的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"

如果有类似需求的人可以参考django-schedule这个项目,它可以实现这个功能。以下是相关链接: https://github.com/thauber/django-schedule/tree/master/schedule/models https://github.com/thauber/django-schedule/


1
关于这个问题,当在models2.py中做类似from product.models import Product的时候,我遇到了另一个问题:ImportError: 没有找到名为models的模块。 - diegueus9
72
为了保持每个文件只有一个类,你可以这样做。 - worc
51
“为什么”是希望减小一个庞大的 models.py 文件的愿望。当我的文件超过 15k 行代码时,我最近也这样做了。文章写得很好。这个过程相当简单。主要的注意点是你必须记住定义一个显式的 app_label,因为 Django 默认从直接模块中提取它。 - Cerin
1
在2016年尝试了这个。这篇文章的第二部分还需要吗?我只是将类移到单独的文件中,编写了我的__init__.py,一切似乎都正常工作。我没有必要更正我的模型文件。我能够从shell和django admin中获取和创建对象。我已经尝试了大约一周的django,所以我想知道最新版本是否允许这种情况发生? - Vic
4
@Vic:在Django的新版本中,Meta类中的app_label已不再是必需的。请参阅https://code.djangoproject.com/wiki/CookBookSplitModelsToFiles - jrial
显示剩余4条评论

11

1
很遗憾,那个链接似乎不再可用 :( - Joao Coelho
1
看起来 Wayback Machine 仍然有它:https://web.archive.org/web/20190331105757/http://paltman.com/breaking-apart-models-in-django/ - Adam Luchjenbroers

9

Django 3的相关链接如下:

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

以前版本的文档链接已失效。示例内容非常简洁,如下所示:

为此,请创建一个models package。删除models.py并创建一个myapp/models/目录,其中包含一个__init__.py文件和存储模型的文件。必须在__init__.py文件中导入这些模型。

例如,在models目录中有organic.py和synthetic.py文件:

from .organic import Person
from .synthetic import Robot

1

最简单的步骤:

  1. 在您的应用程序中创建一个模型文件夹(文件夹名称应为model
  2. 从应用程序目录中删除model.py文件(在删除文件时备份文件)
  3. 然后在模型文件夹中创建init.py文件
  4. 接着在init.py文件中写入简单的一行代码
  5. 然后在您的模型文件夹中创建模型文件,模型文件名应与类名相同,如果类名为“Employee”,则模型文件名应为“employee.py”
  6. 接着在模型文件中定义您的数据库表,就像在model.py文件中写的那样
  7. 保存

我的代码:from django_adminlte.models.employee import Employee

对于您的代码: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

2
这正是他试图解决的问题。在Django 2.x中,这个解决方案会导致RuntimeError ModelX doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS. - radtek

0

我写了一个可能有用的脚本。

github.com/victorqribeiro/splitDjangoModels

它将模型拆分为具有适当命名和导入的单独文件;它还创建了一个init文件,因此您可以一次导入所有模型。

如果这有帮助,请告诉我。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接