Модели
Модель — это единственный и окончательный источник информации о ваших данных. Она содержит основные поля и поведение хранимых данных. Как правило, каждая модель отображается в одной базе данных таблице.
Основные моменты:
- Каждая модель — это класс 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 моделей 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) ['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 , имеющем 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): # __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"
def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
Последний метод в этом примере является свойством.
В ссылке на экземпляр модели есть полный список методов, автоматически предоставляемых каждой модели. Вы можете переопределить большинство из них – см. переопределение предопределённых методов модели ниже – но есть несколько, которые вы почти всегда захотите определить:
-
__str__() (Python 3) -
Python «магический» метод, возвращающий строковое «представление» любого объекта. Именно его Python и Django используют всякий раз, когда экземпляр модели нужно привести к виду обычной строки и отобразить. В частности, это происходит, когда вы отображаете объект в интерактивной консоли или в админке.
Вы всегда должны определить этот метод; по умолчанию он не очень полезен.
-
__unicode__() (Python 2) - Эквивалент метода
__str__()в Python 2. -
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. Эта модель затем не будет использоваться для создания таблиц базы данных. Вместо этого, когда она используется как базовый класс для других моделей, её поля будут добавлены к полям дочернего класса. Ошибка иметь поля в абстрактном базовом классе с тем же именем, что и в дочернем (Django поднимет исключение).
Пример:
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 каждый раз.
Некоторые атрибуты не имеют смысла включать в класс Meta абстрактного базового класса. Например, включение db_table означало бы, что все дочерние классы (те, которые не указывают собственный класс Meta) будут использовать одну и ту же таблицу базы данных, что, почти наверняка, не то, что вы хотите.
Будьте осторожны с 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.
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 будут упорядоченными по last_name.
Прокси-модели наследуют атрибуты Meta так же, как и обычные модели.
QuerySets всё ещё возвращают модель, которая была запрошена
Нет способа заставить 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:
class Piece(models.Model):
pass
class Article(Piece):
...
class Book(Piece):
...
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.10/topics/db/models/