Модели
Модель является единственным и определяющим источником информации о ваших данных. Она содержит необходимые поля и поведение данных, которые вы храните. Как правило, каждая модель отображается в одной таблице базы данных.
Основы:
- Каждая модель представляет собой класс Python, который наследуется от
django.db.models.Model. - Каждое свойство модели представляет собой поле базы данных.
- С учётом всего этого Django предоставляет автоматически сгенерированный API доступа к базе данных; см. Выполнение запросов.
Быстрый пример
Эта примерная модель определяет Person, которая имеет first_name и last_name:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
first_name и last_name являются полями модели. Каждое поле задаётся как свойство класса, и каждое свойство отображается в столбец базы данных.
Вышеуказанная модель Person создаст таблицу базы данных следующего вида:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
Некоторые технические замечания:
- Название таблицы,
myapp_person, автоматически выводится из метаданных модели, но может быть переопределено. Дополнительные сведения см. в Имена таблиц. - Поле
idдобавляется автоматически, но это поведение можно переопределить. См. Автоматические поля первичного ключа. - SQL-код
CREATE TABLEв этом примере отформатирован с использованием синтаксиса PostgreSQL, но стоит отметить, что Django использует SQL, настроенный под базу данных, указанную в файле настроек.
Использование моделей
После определения моделей вам необходимо сообщить Django, что вы собираетесь использовать эти модели. Сделайте это, отредактировав файл настроек и изменив параметр INSTALLED_APPS, чтобы добавить имя модуля, содержащего ваши models.py.
Например, если модели вашего приложения находятся в модуле myapp.models (структура пакета, созданная для приложения с помощью скрипта manage.py startapp), то INSTALLED_APPS должен содержать, частично:
INSTALLED_APPS = [
#...
'myapp',
#...
]
При добавлении новых приложений в INSTALLED_APPS, обязательно запустите manage.py migrate, предварительно, при необходимости, создав миграции для них с помощью manage.py makemigrations.
Поля
Самая важная часть модели — и единственная необходимая часть — это список полей базы данных, которые она определяет. Поля задаются свойствами класса. Будьте внимательны, чтобы не использовать имена полей, которые конфликтуют с API моделей, такими как clean, save, или delete.
Пример:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
Типы полей
Каждое поле в вашей модели должно быть экземпляром соответствующего класса Field. Django использует типы классов полей, чтобы определить несколько вещей:
- Тип столбца, который сообщает базе данных, какой тип данных хранить (например,
INTEGER,VARCHAR,TEXT). - Значение по умолчанию HTML виджета для рендеринга поля формы (например,
<input type="text">,<select>). - Минимальные требования к валидации, используемые в админке Django и в автоматически генерируемых формах.
Django поставляется с десятками встроенных типов полей; полный список можно найти в справочнике по полям моделей. Вы можете легко написать свои поля, если встроенных полей Django недостаточно; см. Как создавать пользовательские поля моделей.
Параметры полей
Каждое поле принимает определённый набор специфичных для поля аргументов (документированных в справочнике по полям моделей). Например, CharField (и его подклассы) требуют аргумента max_length, который определяет размер поля базы данных VARCHAR для хранения данных.
Также существует набор общих аргументов, доступных для всех типов полей. Все они необязательны. Они подробно описаны в справочнике, но здесь приведён краткий обзор наиболее часто используемых:
-
null - Если
True, Django будет хранить пустые значения какNULLв базе данных. Значение по умолчанию —False. -
blank -
Если
True, поле допускает пустые значения. Значение по умолчанию —False.Обратите внимание, что это отличается от
null.null— чисто база данных, аblank— валидация. Если поле имеетblank=True, валидация формы позволит вводить пустое значение. Если поле имеетblank=False, поле является обязательным. -
choices -
Последовательность 2-х кортежей, используемых в качестве выбора для этого поля. Если это задано, виджет формы по умолчанию будет выпадающим списком вместо стандартного текстового поля и будет ограничивать выбор заданными вариантами.
Список выбора выглядит так:
YEAR_IN_SCHOOL_CHOICES = [ ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), ]Примечание
Каждое изменение порядка
choicesсоздаёт новую миграцию.Первый элемент каждого кортежа — это значение, которое будет храниться в базе данных. Второй элемент отображается виджетом поля формы.
Для экземпляра модели значение отображения поля с
choicesможно получить с помощью методаget_FOO_display(). Например:from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
Вы также можете использовать классы перечислений для определения
choicesкомпактным способом:from django.db import models class Runner(models.Model): MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE') name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)Дополнительные примеры доступны в справочнике по полям моделей.
-
default - Значение по умолчанию для поля. Может быть значением или вызываемым объектом. Если вызываемым, он будет вызываться каждый раз при создании нового объекта.
-
help_text - Дополнительный текст «подсказки», который будет отображаться с виджетом формы. Полезно для документации, даже если поле не используется в форме.
-
primary_key -
Если
True, это поле является первичным ключом модели.Если вы не зададите
primary_key=Trueдля любых полей в вашей модели, Django автоматически добавитIntegerFieldдля хранения первичного ключа, поэтому вам не нужно устанавливатьprimary_key=Trueни на одном из ваших полей, если только вы не хотите переопределить поведение первичного ключа по умолчанию. Подробнее см. Автоматические поля первичного ключа.Поле первичного ключа является только для чтения. Если вы измените значение первичного ключа существующего объекта и затем сохраните его, будет создан новый объект наряду со старым. Например:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True)>>> fruit = Fruit.objects.create(name='Apple') >>> fruit.name = 'Pear' >>> fruit.save() >>> Fruit.objects.values_list('name', flat=True) <QuerySet ['Apple', 'Pear']> -
unique - Если
True, это поле должно быть уникальным в пределах таблицы.
Опять же, это лишь краткие описания наиболее распространённых вариантов полей. Полные сведения можно найти в справке по общим параметрам полей модели.
Автоматические поля первичного ключа
По умолчанию Django добавляет к каждой модели поле первичного ключа с автоматическим нарастанием значений, тип которого задаётся для каждого приложения в AppConfig.default_auto_field или глобально в настройке DEFAULT_AUTO_FIELD. Например:
id = models.BigAutoField(primary_key=True)
Если вам нужно указать пользовательский первичный ключ, укажите primary_key=True для одного из ваших полей. Если Django увидит, что вы явно задали Field.primary_key, он не добавит автоматическое поле id.
Каждая модель требует, чтобы ровно одно поле имело primary_key=True (либо явно объявленное, либо автоматически добавленное).
Описание полей с использованием длинных названий
Каждый тип поля, за исключением ForeignKey, ManyToManyField и OneToOneField, может принимать необязательный первый позиционный аргумент — длинное имя поля. Если длинное имя не задано, Django автоматически создаст его, используя имя атрибута поля, преобразуя нижние подчёркивания в пробелы.
В этом примере, длинное имя — "person's first name":
first_name = models.CharField("person's first name", max_length=30)
В этом примере, длинное имя — "first name":
first_name = models.CharField(max_length=30)
ForeignKey, ManyToManyField и OneToOneField требуют, чтобы первым аргументом была модель, поэтому используйте ключевой аргумент verbose_name:
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)
Принято не писать заглавную букву в начале длинного имени verbose_name. Django автоматически добавит её там, где это необходимо.
Связи
Понятно, что мощь реляционных баз данных заключается в связывании таблиц между собой. Django предоставляет способы определения трёх наиболее распространённых типов баз данных: «многие-к-одному», «многие-ко-многим» и «один-к-одному».
Связи «многие-к-одному»
Для определения связи «многие-к-одному» используйте django.db.models.ForeignKey. Вы используете его как любой другой тип Field: включив его в качестве атрибута класса вашей модели.
ForeignKey требует позиционного аргумента: класс, с которым связана модель.
Например, если модель Car имеет связь Manufacturer — то есть, Manufacturer создаёт множество автомобилей, но каждый Car имеет только один Manufacturer — используйте следующие определения:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
Также можно создавать рекурсивные связи (объект со связью «многие-к-одному» с самим собой) и связи с моделями, которые ещё не определены; см. справку по полям моделей для получения подробной информации.
Рекомендуется, но не обязательно, чтобы имя поля ForeignKey (manufacturer в примере выше) совпадало с именем модели в нижнем регистре. Вы можете назвать поле как хотите. Например:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
См. также
ForeignKey поля принимают ряд дополнительных аргументов, которые описаны в справке по полям моделей. Эти параметры помогают определить, как должна работать связь; все они необязательны.
Подробную информацию о доступе к обратным связанным объектам см. в примере Обратная связь между объектами.
Примеры кода см. в примере модели Связи многие-к-одному.
Связи «многие-ко-многим»
Для определения связи «многие-ко-многим» используйте ManyToManyField. Вы используете его как любой другой тип Field: включив его в качестве атрибута класса вашей модели.
ManyToManyField требует позиционного аргумента: класс, с которым связана модель.
Например, если Pizza имеет несколько объектов Topping — то есть, Topping может быть на нескольких пиццах, и каждая Pizza имеет несколько добавок — вот как это можно представить:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
Как и в случае с ForeignKey, вы также можете создавать рекурсивные связи (объект со связью «многие-ко-многим» с самим собой) и связи с моделями, которые ещё не определены.
Рекомендуется, но не обязательно, чтобы имя поля ManyToManyField (toppings в примере выше) было множественным числом, описывающим набор связанных объектов модели.
Неважно, в какой модели находится ManyToManyField, но его нужно разместить только в одной из моделей, а не в обеих.
Как правило, экземпляры ManyToManyField должны быть в объекте, который будет редактироваться в форме. В приведённом выше примере, toppings находится в Pizza (а не в Topping с полем pizzas ManyToManyField ), поскольку естественнее думать о пицце, имеющей добавки, а не об добавках, которые находятся на нескольких пиццах. Таким образом, в форме Pizza пользователи смогут выбирать добавки.
См. также
Полный пример см. в примере модели взаимосвязи многие-ко-многим.
ManyToManyField поля также принимают ряд дополнительных аргументов, которые описаны в справке по полям моделей. Эти параметры помогают определить, как должна работать связь; все они необязательны.
Дополнительные поля в связи многие-ко-многим
Если вы имеете дело только со связями «многие-ко-многим», такими как комбинирование пиццы и добавок, то стандартного ManyToManyField достаточно. Однако иногда может потребоваться связать данные с отношениями между двумя моделями.
Например, рассмотрим приложение, отслеживающее музыкальные группы, к которым принадлежат музыканты. Существует связь «многие-ко-многим» между человеком и группами, к которым он принадлежит, поэтому можно использовать ManyToManyField для представления этой связи. Однако может потребоваться собрать много деталей о членстве, таких как дата вступления человека в группу.
В этих ситуациях Django позволяет указать модель, которая будет управлять отношением «многие ко многим». Затем вы можете добавить дополнительные поля к промежуточной модели. Промежуточная модель связана с ManyToManyField с помощью аргумента through, указывающего на модель, которая будет выступать в качестве посредника. Для нашего примера с музыкантами код будет выглядеть примерно так:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
При настройке промежуточной модели вы явно указываете внешние ключи к моделям, участвующим в отношении «многие ко многим». Это явное объявление определяет, как связаны две модели.
Существует несколько ограничений на промежуточную модель:
- Ваша промежуточная модель должна содержать один и только один внешний ключ к исходной модели (в нашем примере это
Group), или вы должны явно указать внешние ключи, которые Django должен использовать для отношения, используяManyToManyField.through_fields. Если у вас более одного внешнего ключа иthrough_fieldsне указан, будет выведено сообщение об ошибке валидации. Аналогичное ограничение относится к внешнему ключу целевой модели (в нашем примере этоPerson). - Для модели, имеющей отношение «многие ко многим» к самой себе через промежуточную модель, разрешены два внешних ключа к одной и той же модели, но они будут рассматриваться как две (разные) стороны отношения «многие ко многим». Однако, если внешних ключей больше двух, вы также должны указать
through_fieldsкак указано выше, иначе будет выведено сообщение об ошибке валидации.
Теперь, когда вы настроите ManyToManyField для использования вашей промежуточной модели (Membership, в данном случае), вы готовы начать создание отношений «многие ко многим». Это делается путем создания экземпляров промежуточной модели:
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, ... date_joined=date(1962, 8, 16), ... invite_reason="Needed a new drummer.") >>> m1.save() >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>]> >>> ringo.group_set.all() <QuerySet [<Group: The Beatles>]> >>> m2 = Membership.objects.create(person=paul, group=beatles, ... date_joined=date(1960, 8, 1), ... invite_reason="Wanted to form a band.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
Вы также можете использовать add(), create() или set() для создания отношений, если вы укажете through_defaults для всех необходимых полей:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
Возможно, вы предпочтете создавать экземпляры промежуточной модели напрямую.
Если пользовательская таблица, определенная промежуточной моделью, не обеспечивает уникальность пары (model1, model2), позволяя несколько значений, вызов remove() удалит все экземпляры промежуточной модели:
>>> Membership.objects.create(person=ringo, group=beatles, ... date_joined=date(1968, 9, 4), ... invite_reason="You've been gone for a month and we miss you.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]> >>> # This deletes both of the intermediate model instances for Ringo Starr >>> beatles.members.remove(ringo) >>> beatles.members.all() <QuerySet [<Person: Paul McCartney>]>
Метод clear() можно использовать для удаления всех отношений «многие ко многим» для экземпляра:
>>> # Beatles have broken up >>> beatles.members.clear() >>> # Note that this deletes the intermediate model instances >>> Membership.objects.all() <QuerySet []>
После того, как вы установили отношения «многие ко многим», вы можете выполнить запросы. Так же, как и с обычными отношениями «многие ко многим», вы можете выполнять запросы, используя атрибуты модели, связанной «многие ко многим»:
# Find all the groups with a member whose name starts with 'Paul' >>> Group.objects.filter(members__name__startswith='Paul') <QuerySet [<Group: The Beatles>]>
Поскольку вы используете промежуточную модель, вы также можете выполнять запросы по ее атрибутам:
# Find all the members of the Beatles that joined after 1 Jan 1961 >>> Person.objects.filter( ... group__name='The Beatles', ... membership__date_joined__gt=date(1961,1,1)) <QuerySet [<Person: Ringo Starr]>
Если вам нужно получить информацию о членстве, вы можете сделать это, напрямую запросив модель Membership:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
Другой способ получить ту же информацию — это запросить обратное отношение «многие ко многим» из объекта Person:
>>> ringos_membership = ringo.membership_set.get(group=beatles) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
Отношения «один к одному»
Для определения отношения «один к одному» используйте OneToOneField. Вы используете его так же, как и любой другой тип Field, включив его в качестве атрибута класса вашей модели.
Это наиболее полезно для первичного ключа объекта, когда этот объект каким-то образом «расширяет» другой объект.
OneToOneField требует позиционного аргумента: класс, с которым связана модель.
Например, если вы создаёте базу данных «мест», вы создадите стандартные данные, такие как адрес, номер телефона и т.д. в базе данных. Затем, если вы захотите создать базу данных ресторанов на основе мест, вместо того, чтобы повторяться и дублировать эти поля в модели Restaurant, вы можете сделать так, чтобы Restaurant имела OneToOneField к Place (потому что ресторан «является» местом; на самом деле, для этого вы обычно используете наследование, которое подразумевает неявное отношение «один к одному»).
Как и с ForeignKey, можно определить рекурсивное отношение и ссылки на пока не определенные модели.
См. также
См. пример модели отношения «один к одному» для полного примера.
OneToOneField поля также принимают необязательный аргумент parent_link.
OneToOneField классы автоматически становились первичным ключом модели. Теперь это не так (хотя вы можете вручную передать аргумент primary_key, если хотите). Таким образом, теперь можно иметь несколько полей типа OneToOneField в одной модели.
Модели в разных файлах
Совершенно нормально связывать модель с моделью из другого приложения. Для этого импортируйте связанную модель вверху файла, где определена ваша модель. Затем ссылайтесь на другой класс модели там, где это необходимо. Например:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Ограничения на имена полей
Django накладывает некоторые ограничения на имена полей модели:
-
Имя поля не может быть зарезервированным словом Python, потому что это приведет к синтаксической ошибке Python. Например:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word! -
Имя поля не может содержать более одной последовательной подчёркивания, из-за того как работает синтаксис поиска запросов Django. Например:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores! - Имя поля не может заканчиваться подчёркиванием по аналогичным причинам.
Однако эти ограничения можно обойти, потому что имя вашего поля не обязательно должно совпадать с именем столбца вашей базы данных. Обратитесь к опции db_column.
Зарезервированные слова SQL, такие как join, where или select, разрешены в качестве имён полей модели, потому что Django экранирует все имена таблиц и столбцов базы данных в каждом запросе SQL нижележащего уровня. Он использует синтаксис экранирования, соответствующий вашему конкретному движку базы данных.
Настраиваемые типы полей
Если ни одно из существующих полей модели не подходит для ваших целей, или если вы хотите использовать менее распространённые типы столбцов базы данных, вы можете создать свой собственный класс поля. Полное руководство по созданию собственных полей приведено в Как создать пользовательские поля модели.
Опции Meta
Настройте метаданные вашей модели, используя внутренний class Meta, как показано ниже:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
Метаданные модели — это «все, что не является полем», например, параметры сортировки (ordering), имя таблицы базы данных (db_table) или удобочитаемые единственное и множественное число (verbose_name и verbose_name_plural). Никакие не являются обязательными, и добавление class
Meta в модель является необязательным.
Полный список всех возможных Meta опций можно найти в справочнике по параметрам модели.
Атрибуты модели
-
objects - Самым важным атрибутом модели является
Manager. Это интерфейс, через который операции запроса к базе данных предоставляются моделям Django и используется для получения экземпляров из базы данных. Если не определен пользовательскийManager, по умолчанию используется имяobjects. К менеджерам можно получить доступ только через классы моделей, а не через экземпляры моделей.
Методы моделей
Определите пользовательские методы в модели, чтобы добавить пользовательскую функциональность на уровне строки к вашим объектам. В то время как методы Manager предназначены для выполнения действий «по всей таблице», методы моделей должны действовать на конкретный экземпляр модели.
Это полезный метод для размещения бизнес-логики в одном месте — в модели.
Например, эта модель имеет несколько пользовательских методов:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
Последний метод в этом примере является свойством.
В ссылке на экземпляр модели находится полный список методов, автоматически предоставляемых каждой модели. Вы можете переопределить большинство из них — см. переопределение предопределенных методов моделей ниже — но есть несколько, которые вам почти всегда нужно определить:
-
__str__() -
Метод Python «магии», который возвращает строковое представление любого объекта. Это то, что Python и Django будут использовать всякий раз, когда экземпляр модели необходимо преобразовать и отобразить как простую строку. В частности, это происходит при отображении объекта в интерактивной консоли или в админке.
Вы всегда захотите определить этот метод; значение по умолчанию совсем не полезно.
-
get_absolute_url() -
Это сообщает Django, как вычислить URL для объекта. Django использует это в своём админском интерфейсе и всякий раз, когда ему нужно определить URL для объекта.
Любой объект, у которого есть URL, уникально его идентифицирующий, должен определить этот метод.
Переопределение предопределенных методов моделей
Существует ещё один набор методов моделей, которые обобщают множество поведений базы данных, которые вам нужно настроить. В частности, вы часто захотите изменить способ работы save() и delete().
Вы можете свободно переопределять эти методы (и любой другой метод модели), чтобы изменить поведение.
Классический случай использования для переопределения встроенных методов — если вы хотите, чтобы что-то происходило всякий раз, когда вы сохраняете объект. Например (см. save() для документации параметров, которые он принимает):
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
Вы также можете предотвратить сохранение:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(*args, **kwargs) # Call the "real" save() method.
Важно помнить, что необходимо вызывать метод родительского класса — это та часть super().save(*args, **kwargs), — чтобы гарантировать, что объект по-прежнему сохраняется в базе данных. Если вы забудете вызвать метод родительского класса, стандартное поведение не произойдёт, и база данных не будет затронута.
Также важно передавать аргументы, которые могут быть переданы в метод модели — для этого и нужна часть *args, **kwargs. Django время от времени расширяет возможности встроенных методов моделей, добавляя новые аргументы. Если вы используете *args,
**kwargs в определениях своих методов, вы гарантируете, что ваш код автоматически будет поддерживать эти аргументы при их добавлении.
Переопределённые методы модели не вызываются при массовых операциях
Обратите внимание, что метод delete() для объекта необязательно вызывается при удалении объектов в объёме с помощью QuerySet или в результате cascading
delete. Чтобы гарантировать выполнение настроенной логики удаления, можно использовать сигналы pre_delete и/или post_delete.
К сожалению, нет обходного пути, когда creating или updating объектов в объёме, так как ни один из save(), pre_save и post_save не вызываются.
Выполнение пользовательских SQL-запросов
Ещё один распространённый паттерн — написание пользовательских SQL-запросов в методах модели и методах уровня модуля. Подробную информацию об использовании необработанного SQL см. в документации по использованию необработанного SQL.
Наследование моделей
Наследование моделей в Django работает практически так же, как и обычное наследование классов в Python, но базовые принципы в начале страницы всё равно должны соблюдаться. Это означает, что базовый класс должен быть подклассом django.db.models.Model.
Единственное решение, которое вам нужно принять, — это то, хотите ли вы, чтобы родительские модели были моделями сами по себе (с собственными таблицами базы данных) или же родители являются просто контейнерами общей информации, которая будет видна только через дочерние модели.
Существует три стиля наследования, которые возможны в Django.
- Часто вам нужно будет просто использовать родительский класс для хранения информации, которую вам не нужно будет вводить для каждой дочерней модели. Этот класс никогда не будет использоваться изолированно, поэтому вам нужны абстрактные базовые классы.
- Если вы наследуете от существующей модели (возможно, чего-то из другого приложения полностью) и хотите, чтобы каждая модель имела свою таблицу базы данных, наследование с несколькими таблицами — это правильный способ.
- Наконец, если вы хотите только изменить поведение модели на уровне Python, не изменяя поля моделей, вы можете использовать модели-прокси.
Абстрактные базовые классы
Абстрактные базовые классы полезны, когда вы хотите поместить некоторую общую информацию в ряд других моделей. Вы пишете базовый класс и помещаете abstract=True в класс Meta. Эта модель затем не будет использоваться для создания таблицы базы данных. Вместо этого, когда она используется как базовый класс для других моделей, её поля будут добавлены к полям дочернего класса.
Пример:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Модель Student будет иметь три поля: name, age и home_group. Модель CommonInfo не может быть использована как обычная модель Django, так как это абстрактный базовый класс. Она не генерирует таблицу базы данных или менеджер и не может быть создана или сохранена напрямую.
Поля, унаследованные от абстрактных базовых классов, могут быть переопределены другим полем или значением или удалены с помощью None.
Для многих применений этот тип наследования моделей будет именно тем, что вам нужно. Он предоставляет способ факторизации общей информации на уровне Python, при этом всё ещё создавая только одну таблицу базы данных на модель-потомок на уровне базы данных.
Meta наследование
При создании абстрактного базового класса Django делает любой вложенный класс Meta, который вы объявили в базовом классе, доступным как атрибут. Если дочерний класс не объявляет собственный класс Meta, он унаследует класс Meta родителя. Если дочерний класс хочет расширить класс Meta родителя, он может его подклассировать. Например:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
Django вносит одно изменение в класс Meta абстрактного базового класса: перед установкой атрибута Meta, он устанавливает abstract=False. Это означает, что потомки абстрактных базовых классов не автоматически становятся абстрактными классами. Чтобы создать абстрактный базовый класс, наследующий от другого абстрактного базового класса, необходимо явно установить abstract=True у потомка.
Некоторые атрибуты не имеют смысла включать в класс Meta абстрактного базового класса. Например, включение db_table означало бы, что все дочерние классы (которые не указывают свой собственный класс Meta) будут использовать одну и ту же таблицу базы данных, что почти наверняка не то, что вы хотите.
Из-за того, как работает наследование в Python, если дочерний класс наследуется от нескольких абстрактных базовых классов, только параметры Meta из первого перечисленного класса будут унаследованы по умолчанию. Чтобы унаследовать параметры Meta от нескольких абстрактных базовых классов, необходимо явно объявить наследование Meta. Например:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ['name']
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
Будьте внимательны с related_name и related_query_name
Чтобы обойти эту проблему, когда вы используете related_name или related_query_name в абстрактном базовом классе (только), часть значения должна содержать '%(app_label)s' и '%(class)s'.
-
'%(class)s'заменяется на имя дочернего класса в нижнем регистре, в котором используется поле. -
'%(app_label)s'заменяется на имя приложения в нижнем регистре, в котором содержится дочерний класс. Каждое установленное имя приложения должно быть уникальным, и имена классов моделей в каждом приложении также должны быть уникальными, поэтому результирующее имя будет отличаться.
Например, для приложения common/models.py:
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(
OtherModel,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",
)
class Meta:
abstract = True
class ChildA(Base):
pass
class ChildB(Base):
pass
И для другого приложения rare/models.py:
from common.models import Base
class ChildB(Base):
pass
Имя обратной ссылки для поля common.ChildA.m2m будет common_childa_related, а имя обратного запроса — common_childas. Имя обратной ссылки для поля common.ChildB.m2m будет common_childb_related, а имя обратного запроса — common_childbs. Наконец, имя обратной ссылки для поля rare.ChildB.m2m будет rare_childb_related, а имя обратного запроса — rare_childbs. Вам решать, как использовать части '%(class)s' и '%(app_label)s' для построения имени обратной ссылки или имени обратного запроса, но если вы забудете это сделать, Django выдаст ошибки при проверке системы (или при выполнении migrate).
Если вы не укажете атрибут related_name для поля в абстрактном базовом классе, значение по умолчанию для обратного имени будет именем дочернего класса, за которым следует '_set', как это обычно происходит, если вы объявили поле непосредственно в дочернем классе. Например, в приведенном выше коде, если атрибут related_name был опущен, имя обратной ссылки для поля m2m будет childa_set в случае ChildA и childb_set для поля ChildB.
Наследование по нескольким таблицам
Второй тип наследования моделей, поддерживаемый Django, — это когда каждая модель в иерархии является моделью сама по себе. Каждая модель соответствует своей собственной таблице базы данных и может быть запрошена и создана индивидуально. Отношение наследования добавляет связи между дочерней моделью и каждой из ее родительских моделей (через автоматически созданное OneToOneField). Например:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
Все поля Place также будут доступны в Restaurant, хотя данные будут храниться в другой таблице базы данных. Следовательно, оба варианта возможны:
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
Если у вас есть Place, который также является Restaurant, вы можете получить от объекта Place к объекту Restaurant, используя строчную версию имени модели:
>>> p = Place.objects.get(id=12) # If p is a Restaurant object, this will give the child class: >>> p.restaurant <Restaurant: ...>
Однако, если p в приведенном выше примере не был Restaurant (он был создан непосредственно как объект Place или был родителем другого класса), обращение к p.restaurant вызовет исключение Restaurant.DoesNotExist.
Автоматически созданное OneToOneField в Restaurant, связывающее его с Place, выглядит так:
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Вы можете переопределить это поле, объявив собственное OneToOneField с parent_link=True в Restaurant.
Meta и наследование по нескольким таблицам
В ситуации наследования по нескольким таблицам для дочернего класса не имеет смысла наследование класса Meta родительского класса. Все параметры Meta уже применены к родительскому классу, и их повторное применение обычно приведет только к противоречивому поведению (это в отличие от случая с абстрактным базовым классом, где базовый класс не существует сам по себе).
Таким образом, дочерняя модель не имеет доступа к классу Meta родителя. Однако есть несколько ограниченных случаев, когда дочерний класс наследует поведение от родителя: если дочерний класс не указывает атрибут ordering или атрибут get_latest_by, он унаследует их от родителя.
Если у родителя есть упорядочивание, и вы не хотите, чтобы у дочернего класса было какое-либо естественное упорядочивание, вы можете его явно отключить:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
Наследование и обратные отношения
Поскольку наследование по нескольким таблицам использует неявное OneToOneField для связи дочернего и родительского класса, можно переходить от родительского к дочернему классу, как в примере выше. Однако это использует имя, которое является значением по умолчанию для related_name для ForeignKey и ManyToManyField отношений. Если вы помещаете такие отношения на подкласс родительской модели, вы обязательно должны указать атрибут related_name для каждого такого поля. Если вы забудете, Django выдаст ошибку валидации.
Например, используя приведенный выше класс Place снова, давайте создадим другой подкласс с ManyToManyField:
class Supplier(Place):
customers = models.ManyToManyField(Place)
Это приводит к ошибке:
Reverse query name for 'Supplier.customers' clashes with reverse query name for 'Supplier.place_ptr'. HINT: Add or change a related_name argument to the definition for 'Supplier.customers' or 'Supplier.place_ptr'.
Добавление related_name к полю customers следующим образом решит ошибку: models.ManyToManyField(Place, related_name='provider').
Указание поля связи с родителем
Как упоминалось, Django автоматически создаст OneToOneField, связывающий ваш дочерний класс с любыми родительскими моделями, которые не являются абстрактными. Если вы хотите контролировать имя атрибута, связывающего с родительским классом, вы можете создать собственное OneToOneField и установить parent_link=True, чтобы указать, что ваше поле является ссылкой на родительский класс.
Модели-прокси
При использовании наследования с несколькими таблицами, для каждого подкласса модели создается новая таблица базы данных. Это обычно желаемое поведение, так как подклассу нужно место для хранения дополнительных полей данных, отсутствующих в базовом классе. Иногда, однако, вы хотите изменить только поведение модели на Python — например, изменить менеджер по умолчанию или добавить новый метод.
Для этого предназначено наследование моделей-прокси: создание прокси для исходной модели. Вы можете создавать, удалять и обновлять экземпляры модели-прокси, и все данные будут сохранены так, как если бы вы использовали исходную (не проксированную) модель. Разница в том, что вы можете изменять такие вещи, как порядок модели по умолчанию или менеджер по умолчанию в прокси, без изменения исходной модели.
Модели-прокси объявляются как обычные модели. Вы сообщаете Django, что это модель-прокси, установив атрибут proxy класса Meta в значение True.
Например, предположим, что вы хотите добавить метод к модели Person. Вы можете сделать это так:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
Класс MyPerson работает с той же таблицей базы данных, что и его родительский класс Person. В частности, все новые экземпляры Person также будут доступны через MyPerson, и наоборот:
>>> p = Person.objects.create(first_name="foobar") >>> MyPerson.objects.get(first_name="foobar") <MyPerson: foobar>
Вы также можете использовать модель-прокси для определения другого порядка сортировки по умолчанию для модели. Возможно, вам не всегда нужно сортировать модель Person, но регулярно сортировать по атрибуту last_name при использовании прокси:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
Теперь обычные запросы Person будут несортированными, а запросы OrderedPerson будут отсортированы по OrderedPerson.
Модели-прокси наследуют атрибуты Meta так же, как и обычные модели.
QuerySet всё ещё возвращают запрошенную модель
Нет способа заставить Django возвращать, скажем, объект MyPerson всякий раз, когда вы запрашиваете объекты Person. Запрос объектов Person вернёт объекты именно этого типа. Цель прокси-объектов состоит в том, что код, использующий исходную модель Person, будет использовать её, а ваш собственный код может использовать расширения, которые вы добавили (от которых другой код не зависит). Это не способ заменить модель Person (или любую другую) на что-то созданное вами.
Ограничения базового класса
Модель-прокси должна наследоваться ровно от одного не абстрактного класса модели. Нельзя наследоваться от нескольких не абстрактных моделей, так как модель-прокси не предоставляет связи между строками в различных таблицах базы данных. Модель-прокси может наследоваться от любого количества абстрактных классов моделей, при условии, что они не определяют полей модели. Модель-прокси также может наследоваться от любого количества моделей-прокси, которые разделяют общий родительский класс, не являющийся абстрактным.
Менеджеры моделей-прокси
Если вы не указываете менеджеров модели для модели-прокси, она наследует менеджеров от своих родительских моделей. Если вы определяете менеджер для модели-прокси, он станет менеджером по умолчанию, хотя менеджеры, определённые в родительских классах, по-прежнему будут доступны.
Продолжая наш пример выше, вы могли бы изменить менеджер по умолчанию, используемый при запросе модели Person так:
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
Если вы хотите добавить новый менеджер в Прокси, не заменяя существующий менеджер по умолчанию, вы можете использовать методы, описанные в документации по настраиваемым менеджерам: создайте базовый класс, содержащий новые менеджеры, и унаследуйте его после основного базового класса:
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
Вероятно, вам это не понадобится очень часто, но, когда понадобится, это возможно.
Различия между наследованием прокси и не управляемыми моделями
Наследование модели-прокси может выглядеть очень похоже на создание не управляемой модели, используя атрибут managed в классе модели Meta.
С помощью тщательной настройки Meta.db_table вы могли бы создать не управляемую модель, которая дублирует существующую модель и добавляет к ней методы Python. Однако это было бы очень громоздко и уязвимо, поскольку вам нужно будет поддерживать оба варианта синхронизированными, если вы внесёте какие-либо изменения.
С другой стороны, модели-прокси предназначены для точного поведения, как модель, которую они дублируют. Они всегда синхронизированы с родительской моделью, поскольку напрямую наследуют её поля и менеджеры.
Общие правила:
- Если вы дублируете существующую модель или таблицу базы данных и не хотите всех исходных столбцов таблицы базы данных, используйте
Meta.managed=False. Этот вариант обычно полезен для моделирования представлений базы данных и таблиц, которые не находятся под управлением Django. - Если вы хотите изменить только поведенческие атрибуты модели на Python, но сохранить все поля, как в оригинале, используйте
Meta.proxy=True. Это настраивает модель-прокси так, что она является точной копией структуры хранения исходной модели при сохранении данных.
Наследование от множества моделей
Так же, как и в Python, модель Django может наследоваться от нескольких родительских моделей. Имейте в виду, что применяются обычные правила разрешения имён Python. Первый базовый класс, в котором появляется конкретное имя (например, Meta), будет тем, который используется; например, это означает, что если несколько родителей содержат класс Meta, будет использован только первый, а все остальные будут проигнорированы.
В целом, вам не нужно наследоваться от нескольких родителей. Основной случай, где это полезно, — это «миксины»: добавление определённого дополнительного поля или метода ко всем классам, которые наследуют миксин. Старайтесь сохранять иерархии наследования как можно проще и понятнее, чтобы вам не пришлось разбираться, откуда взялась конкретная информация.
Обратите внимание, что наследование от нескольких моделей, имеющих общее поле первичного ключа id , вызовет ошибку. Для корректного использования множественного наследования вы можете использовать явный AutoField в базовых моделях:
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
...
class BookReview(Book, Article):
pass
Или используйте общего предка для хранения AutoField. Это требует использования явного OneToOneField от каждой родительской модели к общему предку, чтобы избежать конфликта между полями, которые автоматически генерируются и наследуются дочерним классом:
class Piece(models.Model):
pass
class Article(Piece):
article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
...
class Book(Piece):
book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
...
class BookReview(Book, Article):
pass
Прямое использование имени поля запрещено
В обычном наследовании классов Python дочерний класс может переопределять любой атрибут родительского класса. В Django это обычно не разрешено для полей модели. Если базовый класс модели, не являющийся абстрактным, имеет поле под названием author, вы не можете создать другое поле модели или определить атрибут под названием author в любом классе, наследующемся от этого базового класса.
Это ограничение не относится к полям модели, унаследованным от абстрактной модели. Такие поля могут быть переопределены другим полем или значением, или удалены путём установки field_name = None.
Предупреждение
Менеджеры моделей наследуются от абстрактных базовых классов. Переопределение унаследованного поля, к которому обращается унаследованный Manager, может привести к скрытым ошибкам. См. настраиваемые менеджеры и наследование моделей.
Примечание
Некоторые поля определяют дополнительные атрибуты модели, например, ForeignKey определяет дополнительный атрибут с _id , добавленным к имени поля, а также related_name и related_query_name в модели-получателе.
Эти дополнительные атрибуты нельзя переопределять, если поле, которое их определяет, не изменено или удалено, чтобы оно больше не определяло дополнительный атрибут.
Переопределение полей в родительской модели приводит к трудностям в таких областях, как инициализация новых экземпляров (указание, какое поле инициализируется в Model.__init__) и сериализация. Это особенности, с которыми нормальное наследование классов Python не сталкивается таким же образом, поэтому разница между наследованием моделей Django и наследованием классов Python не является случайной.
Это ограничение применяется только к атрибутам, которые являются экземплярами Field. Обычные атрибуты Python можно переопределять по желанию. Оно также применяется только к имени атрибута, как его видит Python: если вы вручную указываете имя столбца базы данных, то одно и то же имя столбца может появляться в дочерней и родительской модели для наследования с несколькими таблицами (это столбцы в двух разных таблицах базы данных).
Django поднимет исключение FieldError, если вы переопределите любое поле модели в любой родительской модели.
Обратите внимание, что из-за способа разрешения полей во время определения класса, поля моделей, унаследованные от нескольких абстрактных родительских моделей, разрешаются в строгом порядке поиска по глубине. Это отличается от стандартного Python MRO, который разрешается в ширину в случаях наследования с алмазной формой. Это различие касается только сложных иерархий моделей, которых (как советуется выше) следует избегать.
Организация моделей в пакете
Команда manage.py startapp создаёт структуру приложения, которая включает файл models.py. Если у вас много моделей, организация их в отдельных файлах может быть полезной.
Для этого создайте пакет models. Удалите models.py и создайте директорию myapp/models/ с файлом __init__.py и файлами для хранения ваших моделей. Вы должны импортировать модели в файл __init__.py.
Например, если у вас были organic.py и synthetic.py в директории models.
myapp/models/__init__.pyfrom .organic import Person from .synthetic import Robot
Явное импортирование каждой модели, а не использование from .models import *, имеет преимущества: не засоряет пространство имен, делает код более читаемым и поддерживает полезность инструментов анализа кода.
См. также
- Справочник по моделям
- Охватывает все API, связанные с моделями, включая поля моделей, связанные объекты и
QuerySet.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.1/topics/db/models/