5.11. Models Method Property
@propertyis a Python decorator that allows you to define a method that behaves like an attributeThis fields are not stored in the database
They are calculated when accessed
They are read-only (can't be set)
They can be used in the Django Admin
>>> #
... from datetime import date
... from django.db import models
... from django.utils.translation import gettext_lazy as _
... from shop.models import Address, Email
...
...
... class Customer(models.Model):
... firstname = models.CharField(verbose_name=_('First Name'), max_length=100, null=False, blank=False)
... lastname = models.CharField(verbose_name=_('Last Name'), max_length=100, null=False, blank=False, db_index=True)
...
... @property
... def age(self, year=365.25):
... days = (date.today() - self.birthdate).days
... return int(days / year)
...
... @property
... def emails(self):
... return Email.objects.filter(customer=self)
...
... @property
... def addresses(self):
... return Address.objects.filter(customer=self)
...
... @property
... def changelog(self):
... from django.contrib.contenttypes.models import ContentType
... from django.contrib.admin.models import LogEntry
... model = self.__class__.__name__.lower()
... app_label = self.__class__._meta.app_label
... ct = ContentType.objects.get(app_label=app_label, model=model)
... return LogEntry.objects.filter(content_type=ct, object_id=self.pk)
... def __str__(self): ... return f'{self.firstname} {self.lastname}' ... ... class Meta: ... verbose_name = _('Customer') ... verbose_name_plural = _('Customers')
5.11.1. Assignments
# TODO: Create Tests
# doctest: +SKIP_FILE
# %% About
# - Name: Django Model Property
# - Difficulty: easy
# - Lines: 4
# - Minutes: 5
# %% 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
# %% English
# 0. Use `myproject.myapp`
# 1. Modify model `Person`
# 2. Add property fullname that returns `f'{self.first_name} {self.last_name}'`
# 3. Use `@property` decorator
# 4. Run `makemigrations`
# 5. Run `migrate`
# %% Polish
# 0. Użyj `myproject.myapp`
# 1. Stwórz model `Person`
# 2. Dodaj właściwość fullname, która zwraca `f'{self.first_name} {self.last_name}'`
# 3. Użyj dekoratora `@property`
# 4. Uruchom `makemigrations`
# 5. Uruchom `migrate`
# %% Expected
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 12), \
'Python has an is invalid version; expected: `3.12` or newer.'
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
from django.db import models
from django.utils.translation import gettext_lazy as _
# %% Types
Group: type[models.Model]
# %% Data
# %% Result
class Person(models.Model):
firstname = models.CharField(verbose_name=_('Firstname'), max_length=30, default=None, null=True, blank=True)
lastname = models.CharField(verbose_name=_('Lastname'), max_length=30, null=False, blank=False, db_index=True)
# TODO: Create Tests
# doctest: +SKIP_FILE
# %% About
# - Name: Django Model Property
# - Difficulty: easy
# - Lines: 4
# - Minutes: 5
# %% 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
# %% English
# 0. Use `myproject.myapp`
# 1. Modify model `Person`
# 2. Add property `age` that returns age in years based on `self.birthdate`
# 3. Mind, that field `birthdate` can be `NULL`, then return `None`
# 4. Use `@property` decorator
# 5. Run `makemigrations`
# 6. Run `migrate`
# %% Polish
# 0. Użyj `myproject.myapp`
# 1. Stwórz model `Person`
# 2. Dodaj property `age`, która zwraca wiek w latach na podstawie `self.birthdate`
# 3. Pamiętaj, że pole `birthdate` może być `NULL`, wtedy zwróć `None`
# 4. Użyj dekoratora `@property`
# 5. Uruchom `makemigrations`
# 6. Uruchom `migrate`
# %% Expected
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 12), \
'Python has an is invalid version; expected: `3.12` or newer.'
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
from django.db import models
from django.utils.translation import gettext_lazy as _
# %% Types
Group: type[models.Model]
# %% Data
# %% Result
class Person(models.Model):
birthdate = models.DateField(verbose_name=_('Birthdate'), null=True, blank=True, default=None)