Модели
Модель — это единственный и определяющий источник информации о ваших данных. Она содержит необходимые поля и поведение хранимых данных. Как правило, каждая модель соответствует одной таблице базы данных.
Основы:
- Каждая модель — это класс 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'), )Первый элемент в каждом кортеже — значение, которое будет храниться в базе данных. Второй элемент будет отображён виджетом формы по умолчанию или в
ModelChoiceField. Для экземпляра модели, значение отображения для поля вариантов можно получить, используя метод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'
-
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 предоставляет каждой модели следующее поле:
id = models.AutoField(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 имеет ManyToManyField pizzas ), поскольку естественнее думать о пицце с добавками, чем о добавке на нескольких пиццах. Таким образом, форма 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): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
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так же, как указано выше, в противном случае будет поднято сообщение об ошибке валидации. - При определении отношения «многие ко многим» от модели к себе, используя промежуточную модель, обязательно используйте
symmetrical=False(см. справочник по полям модели).
Теперь, когда вы настроили 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() для создания отношений:
>>> # The following statements will not work >>> beatles.members.add(john) >>> beatles.members.create(name="George Harrison") >>> beatles.members.set([john, paul, ringo, george])
Почему? Вы не можете просто создать отношение между Person и Group — вам нужно указать все детали для отношения, необходимые модели Membership. Простые вызовы add, create и присваивания не предоставляют способа указать эти дополнительные детали. В результате они отключены для отношений «многие ко многим», которые используют промежуточную модель. Единственный способ создания такого отношения — создание экземпляров промежуточной модели.
Метод remove() отключен по аналогичным причинам. Например, если настраиваемая таблица через, определённая промежуточной моделью, не накладывает ограничение уникальности на пару (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 will not work because it cannot tell which membership to remove >>> beatles.members.remove(ringo)
Однако метод 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 3) -
Питон-магический метод, который возвращает строковое «представление» любого объекта. Именно это Python и Django будут использовать всякий раз, когда экземпляр модели нужно преобразовать и отобразить в виде простой строки. В частности, это происходит, когда вы отображаете объект в интерактивной консоли или в админке.
Вы всегда захотите определить этот метод; метод по умолчанию не очень полезен.
-
__unicode__() (Python 2) - Эквивалент Python 2 метода
__str__(). -
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(Blog, self).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(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
Важно помнить о вызове метода суперкласса — это то, что делает super(Blog, self).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, так как она является абстрактным базовым классом. Она не генерирует таблицу базы данных, не имеет менеджера и не может быть инициализирована или сохранена напрямую.
Для многих задач такой тип наследования моделей будет именно тем, что вам нужно. Он предоставляет способ вынесения общей информации на уровне 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 каждый раз.
Некоторые атрибуты не имеют смысла в классе Мета абстрактного базового класса. Например, включение db_table означало бы, что все дочерние классы (которые не указывают свой собственный Мета) будут использовать одну и ту же таблицу базы данных, что, почти наверняка, не является желаемым результатом.
Будьте внимательны с 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.
Добавлена интерполяция '%(app_label)s' и '%(class)s' для related_query_name.
Наследование с несколькими таблицами
Второй тип наследования моделей, поддерживаемый 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,
)
Вы можете переопределить это поле, объявив своё собственное OneToOneField с parent_link=True в Restaurant.
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 будут отсортированы по last_name.
Модели-прокси наследуют атрибуты 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, если вы переопределите какое-либо поле модели в любой родительской модели.
Организация моделей в пакете
Команда manage.py startapp создаёт структуру приложения, которая включает файл models.py. Если у вас много моделей, их организация в отдельных файлах может быть полезной.
Для этого создайте пакет models. Удалите models.py и создайте директорию myapp/models/ с файлом __init__.py и файлами для хранения ваших моделей. Вам необходимо импортировать модели в файл __init__.py.
Например, если у вас были organic.py и synthetic.py в директории models:
from .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/1.11/topics/db/models/