4.19. Models Directory

  • Fat model architecture

  • Single File vs. Models per file

  • Important, add app_label to model's Meta class

  • Important, add __init__.py to the myproject/shop/models/ directory

  • Important, import from .customer import * in myproject/shop/models/__init__.py

4.19.1. Files and Directory Structure

shop/
├── models/
│   ├── __init__.py
│   ├── customer.py
│   ├── address.py
│   ├── order.py
│   ├── product.py
├── admin.py
├── apps.py
├── tests.py
├── views.py

4.19.2. Models

  • One model per file

  • Important, add app_label to model's Meta class

  • Use string in ForeignKey to avoid circular import

  • Supporting classes like choices, relations and helper functions, etc. can be in the same file

File: myproject/shop/models/customer.py

>>> 
... from django.db import models
... from django.utils.translation import gettext_lazy as _
...
...
... class Customer(models.Model):
...     firstname = models.CharField(verbose_name=_('First Name'), max_length=50)
...     lastname = models.CharField(verbose_name=_('Last Name'), max_length=50, db_index=True)
...     email = models.EmailField(verbose_name=_('Email'), max_length=100, null=True, blank=True, default=None, unique=True)
...
...     def __str__(self):
...         return f'{self.firstname} {self.lastname}'
...
...     class Meta:
...         app_label = 'shop'
...         verbose_name = _('Customer')
...         verbose_name_plural = _('Customers')

File: myproject/shop/models/address.py

>>> 
... from django.db import models
... from django.utils.translation import gettext_lazy as _
...
...
... class AddressType(models.TextChoices):
...     HOME = 'home', _('Home')
...     WORK = 'work', _('Work')
...     BILLING = 'billing', _('Billing')
...     SHIPPING = 'shipping', _('Shipping')
...
...
... class Address(models.Model):
...     customer = models.ForeignKey(verbose_name=_('Customer'), to='shop.Customer', on_delete=models.CASCADE, related_name='address')
...     type = models.CharField(verbose_name=_('Type'), choices=AddressType, max_length=20, null=False, blank=False)
...     street = models.CharField(verbose_name=_('Street'), max_length=100, null=False, blank=False)
...     house = models.CharField(verbose_name=_('House Number'), max_length=20, null=False, blank=False)
...     apartment = models.CharField(verbose_name=_('Apartment Number'), max_length=20, null=True, blank=True, default=None)
...     postcode = models.CharField(verbose_name=_('Post Code'), max_length=20, null=False, blank=False)
...     city = models.CharField(verbose_name=_('City'), max_length=100, null=False, blank=False)
...     region = models.CharField(verbose_name=_('Region'), max_length=100, null=True, blank=True, default=None)
...     country = models.CharField(verbose_name=_('Country'), max_length=100, null=False, blank=False)
...
...     class Meta:
...         app_label = 'shop'
...         verbose_name = _('Address')
...         verbose_name_plural = _('Addresses')
...
...     def __str__(self):
...         return f'{self.street} {self.house}, {self.city}'

4.19.3. Init File

  • Important, add __init__.py to the myproject/shop/models/ directory

  • Important, import from .customer import * in myproject/shop/models/__init__.py

File: myproject/shop/models/__init__.py:

from .customer import *
from .address import *
from .order import *
from .product import *

4.19.4. Assignments

# TODO: Create Tests
# doctest: +SKIP_FILE
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Django Model Directory
# - Difficulty: easy
# - Lines: 4
# - Minutes: 5

# %% English
# 0. Use `myproject.demo`
# 1. Split models into separate files in `models` directory
# 2. Create `myproject/shop/models/__init__.py` and import models from files

# %% Polish
# 0. Użyj `myproject.demo`
# 1. Podziel modele na osobne pliki w katalogu `models`
# 2. Stwórz `myproject/shop/models/__init__.py` i zaimportuj modele z pliku

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'
"""

...