When you create a new app, Django creates models.py
, views.py
, and tests.py
files for your code.
Putting all the models, views, or tests in a single file is usually
not a good idea, especially on a team project with many people working
on different features.
A better way is to divide your code into multiple files.
For example, we can separate tests into individual files like this:
polls/
__init__.py
apps.py
models.py
urls.py
views.py
tests/
__init__.py
test_question.py
test_choice.py
test_views.py
Then, if one developer writes new tests for questions it won’t conflict with another developer’s tests for views.
The following sections explain how to do this.
- Divide App Code into Multiple Files shows the structure
- Separate Models how to refactor models into a
models
package - Separate Tests how to refactor tests
- Separate Views how to refactor views
Cookiecutter Django has more ideas on project setup and organization.
Divide App Code into Multiple Files
Here is an example for the ‘polls’ app:
BEFORE AFTER
polls/ polls/
__init__.py __init__.py
apps.py apps.py
forms.py forms.py
models.py models/
__init__.py
question.py
choice.py
vote.py
tests.py tests/
__init__.py
test_question.py
test_choice.py
test_vote.py
test_views.py
views.py views/
index.py
detail.py
results.py
All of these are optional. For example, you can divide models and tests but leave the views in a single file (so you can enjoy merge conflicts :-).
If you make the changes carefully, they are invisible to the rest of your application and you won’t need to change any imports.
You’ll still be able to write from models import Question
exactly as in the original code.
Separate Models
Using the Django polls app as an example
from django.db import models
class Question(models.Model):
# code for Question class
...
class Choice(models.Model):
# code for Choice class
...
-
In the
polls
app, create a subdirectory namedmodels
. This will be a Python “package”. - In
models/
, create one file for each model class and move the code to that file:models/question.py
,models/choice.py
,models/vote.py
.polls/ models/ __init__.py question.py choice.py vote.py
- To make the models package look like the original models module so we can write
from models import Question
, you need some code in__init__.py
. This is critical to making the change transparent to your other code and to Django migrations.# in models/__init__.py from .question import Question from .choice import Choice from .vote import Vote
If there is anything else in
models
that other parts of your code need to import, add those names to__init__.py
as well. - Modify Imports in each Model Model Class.
Inside of choice.py, you need a relative import for other models:
from .question import Question
and similarly for any other imports. Add necessary imports to each model file.
- Run your tests. Do they still pass?
Separate Views
Dividing views into separate files is just like dividing models, but you can combine several view functions in one file if you prefer.
The real benefit is to separate views for different “features” so your team can work on features in branches without merge conflicts.
You will need imports in views/__init__.py
to make the views look like a single module.
polls/
views/
__init__.py (import other views into this file)
index_view.py
details.py
results.py
Separate Tests
Separating tests is easy! You can use an empty __init__.py
file in the tests/
folder, because nothing else needs to import tests. You will need to modify the imports inside the test code.
You should name test files using Python’s naming convention, so they will be autodiscovered. For example: test_question.py
and test_voting.py
.
-
Put your tests in a folder named
tests
, e.g.polls/tests
. -
Move the
tests.py
file to thetests
folder - Divide your tests into separate files based on target or type
- Separate
tests.py
intotest_question.py
(tests for Question),test_index_view.py
(tests for a specific target), etc. - You can divide tests based on type of behavior you want to test, such as
test_authorization.py
.
- Separate
-
Modify imports in the test code files
Since the tests are now in their own package, you will need to modify the imports in your tests.
Old Import in “tests.py”:
# OLD: this won't work from .models import Question, Choice, Vote
New Import:
from polls.models import Question, Choice, Vote
-
Run the Tests and Verify They Work
- Run
manage.py test app_name
to verify that it discovers all tests. - Example:
python manage.py test -v 2 polls
- Run
Reference
Cookbook - Splitting Models across multiple files from the Django code wiki. (2011)
Not Necessary: Meta-class Information for Database Table Names
In Django before version 3, if you refactor model classes into a separate package, it would effect the default table names in the database.
To prevent that, you can specify the table name in the class’s “meta” information.
This is not necessary for Django 3 and newer, but its included here for reference. It might be useful for other situations.
From this Django wiki page, the issue was:
Django creates a table for each model class using a table name in the form app_classname
, such as polls_question
and polls_choice
. When the model class is in a subfolder, it would infer the app name incorrectly and misname the tables (e.g. polls_models_question
).
To preserve the default table names, add a class Meta
inside each model class with 2 values:
db_table
= name of the database tableapp_label
= name of the “app” for this table
# File: polls/models/question.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=80)
class Meta:
"""
See https://code.djangoproject.com/wiki/CookBookSplitModelsToFiles
"""
db_table = "polls_question"
app_label = "polls"
Similarly for other models.