Модели отображают информацию о данных, с которыми вы работаете. Они содержат поля и поведение ваших данных. Обычно одна модель представляет одну таблицу в базе данных.
Основы:
django.db.models.Model.Вот пример модели, которая определяет гипотетического человека(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 добавлено автоматически, но его также можно переопределить. Подробнее Первичный ключ по умолчанию.CREATE TABLE SQL в этом примере соответствует синтаксису 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).<input type="text">, <select>).В Django есть большое количество полей; полный список можно посмотреть на странице списка полей. Вы можете легко добавить собственное поле; смотрите Создание собственных полей для модели.
Для каждого поля есть набор предопределенных аргументов (описание на странице описания полей). Например, CharField (и унаследованные от него) имеют обязательный аргумент max_length, который определяет размер поля VARCHAR для хранения данных этого поля.
Также есть список стандартных аргументов для всех полей. Все они не обязательны. Все они описаны в разделе про аргументы полей модели, вот список самых используемых:
nullTrue, Django сохранит пустое значение как NULL в базе данных. По умолчанию - False.blankЕсли True, поле не обязательно и может быть пустым. По умолчанию - False.
Это не то же что и null. null относится к базе данных, blank - к проверке данных. Если поле содержит blank=True, форма позволит передать пустое значение. При blank=False - поле обязательно.
choicesA sequence of 2-tuples to use as choices for this field. If this is given, the default form widget will be a select box instead of the standard text field and will limit choices to the choices given.
Список значений выглядит:
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
Примечание
A new migration is created each time the order of choices changes.
The first element in each tuple is the value that will be stored in the database. The second element is displayed by the field’s form widget.
Given a model instance, the display value for a field with choices can
be accessed using the get_FOO_display()
method. For example:
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'
You can also use enumeration classes to define choices in a concise
way:
from django.db import models
class Runner(models.Model):
MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
Further examples are available in the model field reference.
defaulthelp_textprimary_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']>
uniqueTrue поле будет уникальным.Это краткое описание самых используемых аргументов. Полный список можно найти на странице описания аргументов поля модели.
По умолчанию Django для каждой модели добавляет такое поле:
id = models.AutoField(primary_key=True)
Это автоинкрементный первичный ключ.
If you’d like to specify a custom primary key, specify
primary_key=True on one of your fields. If Django
sees you’ve explicitly set Field.primary_key, it won’t add the automatic
id column.
Каждая модель должна иметь хотя бы одно поле с 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 первым аргументом принимает класс модели, поэтому используется keyword аргумент 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",
)
Django не делает первую букву прописной для verbose_name - только там, где это необходимо.
Основное преимущество реляционных баз данных - возможность добавлять связи для таблиц. 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)
As with ForeignKey, you can also create
recursive relationships (an object with a
many-to-many relationship to itself) and relationships to models not yet
defined.
Желательно, но не обязательно, чтобы название поля ManyToManyField (toppings в нашем примере) было множественным называнием связанных объектов.
Не имеет значения какая модель содержит поле ManyToManyField, но вы должны добавить его только для одной модели.
Обычно, ManyToManyField необходимо добавить в модель, которая будет редактироваться в форме. В примере выше, toppings добавлено в Pizza (вместо того, чтобы добавить поле pizzas типа ManyToManyField в модель Topping), потому что обычно думают о пицце с ингредиентами, а не об ингредиентах в различных пиццах. В примере выше, форма для Pizza позволит пользователям редактировать ингредиенты для пиццы.
См.также
Примеры использования связи многие-ко-многим можно найти в соответствующем разделе
Поле ManyToManyField принимает список дополнительных аргументов, подробнее в разделе справки о полях модели. Эти настройки помогают определить как работает связь, все они не обязательны.
When you’re only dealing with many-to-many relationships such as mixing and
matching pizzas and toppings, a standard
ManyToManyField is all you need. However, sometimes
you may need to associate data with the relationship between two models.
Например, разберем приложение о музыкальных группах и музыкантах. Для хранения связи между музыкантами и группами мы можем использовать поле ManyToManyField. Но нам также необходимо хранить дополнительную информацию, например, когда музыкант вступил в группу.
Для таких случаев Django позволяет определить модель для хранения связи многие-ко-многим и дополнительной информации. Теперь вы можете добавить дополнительные поля в эту модель. Эту промежуточную модель можно указать в поле ManyToManyField используя аргумент through, который указывает на промежуточную модель. Для нашего примера код будет приблизительно таким:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
В промежуточной модели необходимо добавить внешние ключи на модели, связанные отношением многие-ко-многим. Эти ключи указывают как связаны модели.
Есть несколько ограничений для промежуточной модели:
Group), или вы должны явно указать Django какую связь использовать через параметр ManyToManyField.through_fields. Если промежуточная модель содержит несколько связей с исходной модель и through_fields не указан, будет вызвана ошибка валидации. Аналогичные правила выполняются и для связанной модели (в нашем примере – Person).through_fields, иначе получите ошибку валидации.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>]>
You can also use add(), create(), or set() to create relationships,
as long as you specify through_defaults for any required fields:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
You may prefer to create instances of the intermediate model directly.
If the custom through table defined by the intermediate model does not enforce
uniqueness on the (model1, model2) pair, allowing multiple values, the
remove() call will
remove all intermediate model instances:
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
The clear()
method can be used to remove all many-to-many relationships for an instance:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
Once you have established the many-to-many relationships, you can issue queries. Just as with normal many-to-many relationships, you can query using the attributes of the many-to-many-related model:
# 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 (т.к. ресторан «это» строение; вы можете использовать наследование моделей, которое на самом деле работает через связь один-к-одному).
As with ForeignKey, a recursive relationship can be defined and references to as-yet
undefined models can be made.
См.также
Примеры можно найти в этом разделе.
OneToOneField fields also accept an optional
parent_link argument.
Раньше OneToOneField автоматически использовались как первичный ключ. Теперь это не так (но вы можете сами указать это используя аргумент primary_key). Таким образом вы можете иметь несколько связей OneToOneField к одной модели.
It’s perfectly OK to relate a model to one from another app. To do this, import the related model at the top of the file where your model is defined. Then, refer to the other model class wherever needed. For example:
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 places some restrictions on model field names:
Название поля не может быть слово зарезервированное 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!
A field name cannot end with an underscore, for similar reasons.
Эти ограничения не сложно соблюдать т.к. название поля не обязано быть таким же, как и название колонки в базе данных. Смотрите аргумент db_column.
Зарезервированные SQL слова, такие как join, where или select, можно использовать как название поля, потому что Django экранирует название таблиц и полей для каждого SQL запроса. Используются «кавычки»(quoting syntax) базы данных, которую вы используете.
Если ни одно существующее поле не удовлетворяет вашим потребностям, или вам необходимо использовать какие-либо особенности поля, присущие определенной базе данных - вы можете создать собственный тип поля. Подробнее вы можете прочитать в Создание собственных полей для модели.
Meta options¶Дополнительные настройки для модели можно определить через 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), или human-readable название в единственной и множественной форме(verbose_name и verbose_name_plural). Все они не обязательны и добавлять class Meta тоже не обязательно.
Полный список опций для Meta можно найти в разделе о настройках модели.
objectsManager. Это интерфейс, через который Django выполняет запросы к базе данных и получает объекты. Если собственный Manager не указан, название по умолчанию будет objects. Менеджеры доступны только через класс модели, они не доступны в экземплярах модели.Для добавления функционала работы с экземпляром модели(«row-level» functionality), необходимо просто добавить метод в модель. В то время, как методы 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)
Последний метод в примере - свойство(property).
Раздел о моделях содержит полный список методов, автоматически добавляемых в модель. Вы можете переопределить большинство из них – смотрите Переопределение методов модели, – но есть методы, которые вы чаще всего определите для каждой модели:
__str__()A Python «magic method» that returns a string representation of any object. This is what Python and Django will use whenever a model instance needs to be coerced and displayed as a plain string. Most notably, this happens when you display an object in an interactive console or in the admin.
Желательно определить этот метод, т.к. значение по умолчанию не слишком привлекательно.
get_absolute_url()Этот метод указывает Django, какой URL использовать для объекта. Django использует его в интерфейсе администратора и каждый раз, когда необходимо получить URL для объекта.
Каждый объект, который имеет уникальный URL должен иметь этот метод.
Существуют другие методы модели, которые инкапсулируют работу с базой данных. Чаще всего вы захотите переопределить метод save() и delete().
Вы можете переопределить эти методы и любые другие методы модели.
Распространенная задача, это выполнить какое-либо действие после сохранения объекта. Например (принимаемые параметры смотрите save()):
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
Вы также можете отменить сохранение:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(*args, **kwargs) # Call the "real" save() method.
It’s important to remember to call the superclass method – that’s
that super().save(*args, **kwargs) business – to ensure
that the object still gets saved into the database. If you forget to
call the superclass method, the default behavior won’t happen and the
database won’t get touched.
Также не забывайте о аргументах, которые принимают встроенные методы – вам поможет *args и **kwargs. Django иногда использует дополнительные аргументы для методов. Используя *args, **kwargs, можно быть уверенным, что все аргументы будут приняты.
Переопределенные методы модели не вызываются при множественных операциях(bulk)
Учтите, метод delete() не обязательно вызывается при массовом удалении объектов через QuerySet, или как результат каскадного удаления. Для гарантированного выполнения действий после удаления объекта используйте сигналы pre_delete и/или post_delete.
К сожалению, вы не сможете изменить логику сохранения объектов при использовании creating или updating, так как save(), pre_save и post_save не будут выполнены.
Запросы на чистом SQL лучше выполнять в методе модели. Подробнее о SQL-запросах можно прочитать в разделе о запросах на чистом SQL.
Наследование моделей в Django работает почти так же, как и наследование классов в Python, но следует соблюдать правила, описанные выше. Это означает, что базовый класс должен наследоваться от django.db.models.Model.
Единственное, что вам нужно определить, это должна ли родительская модель быть независимой моделью (с собственной таблицей в базе данных), или же родительская модель просто контейнер для хранения информации, доступной только через дочерние модели.
Существует три вида наследования моделей в Django.
Abstract base classes are useful when you want to put some common
information into a number of other models. You write your base class
and put abstract=True in the Meta
class. This model will then not be used to create any database
table. Instead, when it is used as a base class for other models, its
fields will be added to those of the child class.
Например:
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, т.к. это абстрактный класс. Она не имеет собственной таблицы в базе данных и не имеет менеджера, нельзя создать экземпляр модели и сохранить его в базе данных.
Fields inherited from abstract base classes can be overridden with another
field or value, or be removed with None.
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
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 для абстрактной модели: перед добавлением в модель изменяет атрибут abstract=False. Это означает что дочерняя модель, наследуя Meta, не становится сама абстрактной моделью. Конечно вы можете создать абстрактную модель, которая наследуется от другой абстрактной модели. Вам только нужно определить атрибут abstract=True.
Некоторые атрибуты не имеет смысла добавлять в класс Meta абстрактной модели. Например, добавление db_table означает, что каждая дочерняя модель(каждая, которая не определяет свой собственный класс Meta) будет использовать туже таблицу в базе данных, что точно не то, что вам нужно.
Это второй тип наследования в 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")
If you have a Place that is also a Restaurant, you can get from the
Place object to the Restaurant object by using the lowercase version of
the model name:
>>> 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.
The automatically-created OneToOneField on
Restaurant that links it to Place looks like this:
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
You can override that field by declaring your own
OneToOneField with parent_link=True on Restaurant.
Meta и multi-table наследование¶При multi-table наследовании, не имеет смысла дочерней модели наследовать Meta от родительской. Все атрибуты класса Meta уже используются в родительской модели и использование их снова может привести к противоречивому поведению (это отличие от абстрактной модели, которая сама по себе не существует как независимая модель).
Поэтому дочерняя модель не имеет доступа к родительскому классу Meta. Но есть исключения, когда дочерняя модель наследует поведение родительской: если дочерняя модель не определяет атрибут ordering или get_latest_by, они будут унаследованы.
Если родительская модель определяет сортировку, но вы не хотите ее наследовать в дочерней модели, вы можете указать это таким способом:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
Т.к. multi-table наследование использует 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 чтобы указать, что это поле является связью с родительской моделью.
При использовании multi-table наследования, будет создана новая таблица в базе данных. Это обязательное требование, т.к. дочерней модели необходимо хранить дополнительные поля. Иногда вам необходимо изменить поведение модели на уровне Python – переопределить менеджер по умолчанию или добавить новые методы.
Вот для чего используют proxy-модель: создать proxy для оригинальной модели. Вы можете создать, изменить или обновить объект proxy модели и все изменения будут сохранены так же, как и при изменении оригинальной(non-proxied) модели. Разница в том, что вы можете изменить сортировку по-умолчанию или менеджер по умолчанию в proxy-модели, без изменения оригинальной модели.
Proxy-модели создаются так же, как и обычная модель. Указать что это proxy-модель можно установив атрибут 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>
You could also use a proxy model to define a different default ordering on
a model. You might not always want to order the Person model, but regularly
order by the last_name attribute when you use the proxy:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
Теперь запросы через Person будут не отсортированы а для модели OrderedPerson будут отсортированы по полю last_name.
Proxy models inherit Meta attributes in the same way as regular
models.
QuerySets still return the model that was requested¶Нет способа указать Django возвращать объекты модели MyPerson при запросе через модель Person. QuerySet для модели Person вернет объекты этого типа. Стоит помнить, что код, который использует модель Person, будет использовать ее независимо от добавления proxy-модели. Вы не можете полностью заменить модель Person (или любую другую) всюду, где она используется.
A proxy model must inherit from exactly one non-abstract model class. You can’t inherit from multiple non-abstract models as the proxy model doesn’t provide any connection between the rows in the different database tables. A proxy model can inherit from any number of abstract model classes, providing they do not define any model fields. A proxy model may also inherit from any number of proxy models that share a common non-abstract parent class.
Если вы не определите ни один менеджер для proxy-модели, он будет унаследован от родительской модели. Если вы определите менеджер, он будет использован как менеджер по умолчанию, в то же время доступны менеджеры, определенные в родительской модели.
Используя пример выше, вы можете переопределить менеджер по умолчанию, используемый для получения данных модели 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
Скорее всего вам это не понадобится, но если все таки понадобится, знайте, что это возможно.
Proxy model inheritance might look fairly similar to creating an unmanaged
model, using the managed attribute on a
model’s Meta class.
With careful setting of Meta.db_table you could create an unmanaged model that
shadows an existing model and adds Python methods to it. However, that would be
very repetitive and fragile as you need to keep both copies synchronized if you
make any changes.
On the other hand, proxy models are intended to behave exactly like the model they are proxying for. They are always in sync with the parent model since they directly inherit its fields and managers.
The general rules are:
Meta.managed=False. Также это очень полезно для использования таблиц базы данных, которые управляются не Django.Meta.proxy=True. Proxy-модель воспринимается как точная копия оригинальной при сохранении данных.Так же, как и наследование в Python, можно использовать множественное наследование моделей Django. Имейте в виду, что используются правила именования Python. Например, есть несколько родительских объектов с классом Meta, в таком случае будет использован атрибут первой родительской модели, остальные будут проигнорированы.
В большинстве случаев вам не нужно будет использовать множественное наследование. В основном множественное наследование используют для «mix-in» классов: добавление дополнительных полей и методов для каждой модели унаследованной от mix-in класса. Старайтесь содержать иерархию наследования настолько простой и понятной, насколько это возможно, чтобы не возникало проблем с определением, откуда взялась та или другая информация.
Обратите внимание, наследование от нескольких моделей, которые содержат первичное поле 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
Or use a common ancestor to hold the AutoField. This
requires using an explicit OneToOneField from each
parent model to the common ancestor to avoid a clash between the fields that
are automatically generated and inherited by the child:
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
In normal Python class inheritance, it is permissible for a child class to
override any attribute from the parent class. In Django, this isn’t usually
permitted for model fields. If a non-abstract model base class has a field
called author, you can’t create another model field or define
an attribute called author in any class that inherits from that base class.
This restriction doesn’t apply to model fields inherited from an abstract
model. Such fields may be overridden with another field or value, or be removed
by setting field_name = None.
Предупреждение
Model managers are inherited from abstract base classes. Overriding an
inherited field which is referenced by an inherited
Manager may cause subtle bugs. See custom
managers and model inheritance.
Примечание
Some fields define extra attributes on the model, e.g. a
ForeignKey defines an extra attribute with
_id appended to the field name, as well as related_name and
related_query_name on the foreign model.
These extra attributes cannot be overridden unless the field that defines it is changed or removed so that it no longer defines the extra attribute.
Переопределение полей родительской модели приводит к проблемам при создании модели (нельзя точно узнать, какое поле таблицы указывается в Model.__init__) и при сериализации. При наследовании классов в Python работает все немного по другому.
Эти ограничения относятся только для атрибутов, которые являются экземплярами Field. Остальные атрибуты могут быть переопределены. Это также относится к атрибутам классов. Вы можете указать одинаковое название поля в таблице БД для родительской и дочерней моделей т.к. они находятся в разных таблицах.
Django вызовет исключение FieldError, если вы переопределите поле родительской модели.
The manage.py startapp command creates an application
structure that includes a models.py file. If you have many models,
organizing them in separate files may be useful.
To do so, create a models package. Remove models.py and create a
myapp/models/ directory with an __init__.py file and the files to
store your models. You must import the models in the __init__.py file.
For example, if you had organic.py and synthetic.py in the models
directory:
from .organic import Person
from .synthetic import Robot
Explicitly importing each model rather than using from .models import *
has the advantages of not cluttering the namespace, making code more readable,
and keeping code analysis tools useful.
См.также
нояб. 23, 2021