Модели ¶
Модель - это единственный исчерпывающий источник информации о ваших данных. Он содержит основные поля и поведение данных, которые вы храните. Как правило, каждая модель отображается в одну таблицу базы данных.
Основы:
- Каждая модель представляет собой подклассы Python
django.db.models.Model
. - Каждый атрибут модели представляет собой поле базы данных.
- При всем этом Django предоставляет автоматически сгенерированный API доступа к базе данных; см. Выполнение запросов .
Быстрый пример ¶
В этом примере модели определяется a 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 в этом примере отформатирован с использованием синтаксиса PostgreSQL, но стоит отметить , Django использует SQL специально для хранения базы данных , указанных в вашем файле настроек .
CREATE TABLE
Использование моделей ¶
После того, как вы определили свои модели, вам нужно сообщить Django, что вы собираетесь использовать
эти модели. Сделайте это, отредактировав файл настроек и изменив
INSTALLED_APPS
настройку, добавив имя модуля, который содержит ваш models.py
.
Например, если модели для вашего приложения myapp.models
находятся в модуле
(структура пакета, которая создается для приложения с помощью сценария),
следует частично читать:manage.py startapp
INSTALLED_APPS
INSTALLED_APPS = [
#...
'myapp',
#...
]
Когда вы добавляете новые приложения INSTALLED_APPS
, обязательно запустите их
, при желании сначала сделав для них миграции с .manage.py migrate
manage.py makemigrations
Поля ¶
Самая важная часть модели - и единственная необходимая часть модели - это список полей базы данных, которые она определяет. Поля задаются атрибутами класса. Будьте осторожны , чтобы не выбирать имена полей , которые конфликтуют с
моделями API , как clean
, save
или
delete
.
Пример:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
Типы полей ¶
Каждое поле в вашей модели должно быть экземпляром соответствующего
Field
класса. Django использует типы классов полей для определения нескольких вещей:
- Тип столбца, который указывает базу данных , какие данные в хранилище (например
INTEGER
,VARCHAR
,TEXT
). - Виджет HTML по умолчанию для использования при рендеринге поля формы (например , ).
<input type="text">
<select>
- Минимальные требования к валидации, используемые в админке Django и в автоматически сгенерированных формах.
Django поставляется с десятками встроенных типов полей; вы можете найти полный список в справочнике по полю модели . Вы можете легко написать свои собственные поля, если встроенные в Django не помогают; см. Написание настраиваемых полей модели .
Параметры поля ¶
Каждое поле принимает определенный набор аргументов, зависящих от поля (задокументированных в
справочнике по полям модели ). Например,
CharField
(и его подклассы) требуют
max_length
аргумента, который указывает размер поля VARCHAR
базы данных, используемого для хранения данных.
Также есть набор общих аргументов, доступных для всех типов полей. Все не обязательны. Они полностью объяснены в справочнике , но вот краткое изложение наиболее часто используемых:
null
- Если
True
, Django будет хранить пустые значения, какNULL
в базе данных. По умолчаниюFalse
. blank
Если
True
, поле может быть пустым. По умолчаниюFalse
.Обратите внимание, что это отличается от
null
.null
относится исключительно к базе данных, тогда какblank
- к проверке. Если поле имеетblank=True
, проверка формы позволит ввести пустое значение. Если в поле естьblank=False
, поле будет обязательным.choices
Последовательность из 2-кортежей , чтобы использовать в качестве вариантов для этой области. Если это задано, виджет формы по умолчанию будет полем выбора вместо стандартного текстового поля и ограничит выбор предоставленными вариантами.
Список вариантов выглядит так:
YEAR_IN_SCHOOL_CHOICES = [ ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), ]
Примечание
Новая миграция создается каждый раз при
choices
изменении порядка изменения.Первый элемент в каждом кортеже - это значение, которое будет храниться в базе данных. Второй элемент отображается виджетом формы поля.
Учитывая экземпляр модели,
choices
можно получить доступ к отображаемому значению для поля с помощьюget_FOO_display()
метода. Например:from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
Вы также можете использовать классы перечисления для
choices
краткого определения:from django.db import models class Runner(models.Model): MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE') name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
Дополнительные примеры доступны в справочнике по полям модели .
default
- Значение по умолчанию для поля. Это может быть значение или вызываемый объект. Если вызываемый, он будет вызываться каждый раз при создании нового объекта.
help_text
- Дополнительный текст «справки», отображаемый в виджете формы. Это полезно для документации, даже если ваше поле не используется в форме.
primary_key
Если
True
это поле является первичным ключом для модели.Если вы не укажете
primary_key=True
какие-либо поля в своей модели, Django автоматически добавит элементIntegerField
для хранения первичного ключа, поэтому вам не нужно устанавливатьprimary_key=True
какие-либо поля, если вы не хотите переопределить поведение первичного ключа по умолчанию. Дополнительные сведения см. В разделе Автоматические поля первичного ключа .Поле первичного ключа доступно только для чтения. Если вы измените значение первичного ключа на существующем объекте, а затем сохраните его, новый объект будет создан рядом со старым. Например:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple') >>> fruit.name = 'Pear' >>> fruit.save() >>> Fruit.objects.values_list('name', flat=True) <QuerySet ['Apple', 'Pear']>
unique
- Если
True
, это поле должно быть уникальным во всей таблице.
Опять же, это всего лишь краткое описание наиболее распространенных параметров поля. Полную информацию можно найти в справочнике по опциям полей общей модели .
Автоматические поля первичного ключа ¶
По умолчанию Django предоставляет каждой модели автоматически увеличивающийся первичный ключ с типом, указанным для каждого приложения в AppConfig.default_auto_field
или глобально в
DEFAULT_AUTO_FIELD
настройке. Например:
id = models.BigAutoField(primary_key=True)
Если вы хотите указать настраиваемый первичный ключ, укажите его
primary_key=True
в одном из полей. Если Django увидит, что вы явно установили Field.primary_key
, он не будет добавлять автоматический
id
столбец.
Каждая модель требует наличия ровно одного поля primary_key=True
(либо явно объявленного, либо автоматически добавленного).
В более старых версиях автоматически создаваемые поля первичного ключа всегда были
AutoField
s.
Подробные имена полей ¶
Каждый тип поля, за исключением 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
- то есть, a
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
требуется позиционный аргумент: класс, к которому относится модель.
Например, если a Pizza
имеет несколько Topping
объектов - то есть
Topping
может быть на нескольких пиццах, и у каждого Pizza
есть несколько начинок - вот как вы это представите:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
Как и в случае с ForeignKey
, вы также можете создавать
рекурсивные отношения (объект с отношением «многие ко многим» с самим собой) и отношения с еще не определенными моделями .
Предлагается, но не обязательно, чтобы имя a
ManyToManyField
( toppings
в приведенном выше примере) было множественным числом, описывающим набор связанных объектов модели.
Неважно, в какой модели он установлен
ManyToManyField
, но вы должны использовать его только в одной из моделей, а не в обеих.
Как правило, ManyToManyField
экземпляры должны входить в объект, который будет редактироваться в форме. В приведенном выше примере
toppings
используется Pizza
(вместо того, чтобы Topping
иметь pizzas
ManyToManyField
), потому что более естественно думать о пицце с начинкой, чем о добавке к нескольким пиццам. Как указано выше, Pizza
форма позволяет пользователям выбирать начинки.
Смотрите также
См. Полный пример в примере модели отношений « многие ко многим» .
ManyToManyField
Поля также принимают ряд дополнительных аргументов, которые описаны в справочнике по полям модели . Эти параметры помогают определить, как должны работать отношения; все необязательны.
Дополнительные поля для отношений "многие ко многим" ¶
Когда вы имеете дело только с отношениями «многие ко многим», такими как смешивание и сопоставление пиццы и начинки, стандарт
ManyToManyField
- это все, что вам нужно. Однако иногда может потребоваться связать данные с отношениями между двумя моделями.
Например, рассмотрим случай приложения, отслеживающего музыкальные группы, к которым принадлежат музыканты. Между человеком и группами, членами которых он является, существует связь "многие ко многим", поэтому вы можете использовать a
ManyToManyField
для представления этой связи. Однако есть много деталей о членстве, которые вы, возможно, захотите собрать, например, дата, когда человек присоединился к группе.
В таких ситуациях Django позволяет указать модель, которая будет использоваться для управления отношениями «многие ко многим». Затем вы можете добавить дополнительные поля в промежуточную модель. Промежуточная модель связана с
ManyToManyField
использованием
through
аргумента, чтобы указать на модель, которая будет действовать как посредник. Для нашего примера с музыкантом код будет выглядеть примерно так:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
При настройке модели-посредника вы явно указываете внешние ключи для моделей, которые участвуют в отношении «многие ко многим». Это явное объявление определяет, как связаны две модели.
На промежуточную модель есть несколько ограничений:
- Ваша промежуточная модель должна содержать один и только один внешний ключ к исходной модели (это будет
Group
в нашем примере), или вы должны явно указать внешние ключи, которые Django должен использовать для использования связиManyToManyField.through_fields
. Если у вас более одного внешнего ключа, иthrough_fields
он не указан, возникнет ошибка проверки. Аналогичное ограничение применяется к внешнему ключу целевой модели (это будетPerson
в нашем примере). - Для модели, которая имеет отношение «многие ко многим» через посредническую модель, разрешены два внешних ключа к одной и той же модели, но они будут рассматриваться как две (разные) стороны отношения «многие ко многим». Если существует более двух внешних ключей, вы также должны указать,
through_fields
как указано выше, иначе возникнет ошибка проверки.
Теперь, когда вы настроили ManyToManyField
использование своей модели-посредника ( Membership
в данном случае), вы готовы приступить к созданию некоторых отношений «многие ко многим». Вы делаете это, создавая экземпляры промежуточной модели:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
Вы также можете использовать add()
,
create()
или
set()
для создания отношений, если вы укажете through_defaults
в любых обязательных полях:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
Вы можете предпочесть создавать экземпляры промежуточной модели напрямую.
Если настраиваемая сквозная таблица, определенная промежуточной моделью, не обеспечивает уникальность пары, позволяя использовать несколько значений,
вызов удалит все экземпляры промежуточной модели:(model1, model2)
remove()
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
Этот clear()
метод можно использовать для удаления всех отношений «многие ко многим» для экземпляра:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
После того, как вы установили отношения «многие ко многим», вы можете отправлять запросы. Как и в случае с обычными отношениями «многие-ко-многим», вы можете запросить, используя атрибуты модели «многие-ко-многим»:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>
Поскольку вы используете промежуточную модель, вы также можете запросить ее атрибуты:
# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
... group__name='The Beatles',
... membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>
Если вам нужно получить доступ к информации о членстве, вы можете сделать это, напрямую запросив Membership
модель:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
Другой способ получить доступ к той же информации - запросить
обратную связь многие-ко-многим у
Person
объекта:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
Индивидуальные отношения ¶
Чтобы определить взаимно однозначное отношение, используйте
OneToOneField
. Вы используете его так же, как и любой другой
Field
тип: включив его как атрибут класса вашей модели.
Это наиболее полезно для первичного ключа объекта, когда этот объект каким-то образом «расширяет» другой объект.
OneToOneField
требуется позиционный аргумент: класс, к которому относится модель.
Например, если вы создавали базу данных «мест», вы бы создали в ней довольно стандартные вещи, такие как адрес, номер телефона и т. Д. Затем, если вы хотите создать базу данных ресторанов поверх мест, вместо того, чтобы повторять себя и реплицировать эти поля в Restaurant
модели, вы могли бы сделать так Restaurant
, OneToOneField
чтобы Place
(потому что ресторан «является» местом; фактически, для обработки это обычно используется
наследование , которое включает неявное взаимно-однозначное отношение).
Как и в случае ForeignKey
, можно определить рекурсивную связь и сделать ссылки на еще не определенные модели .
Смотрите также
См. Полный пример в примере модели отношений «один к одному» .
OneToOneField
поля также принимают необязательный
parent_link
аргумент.
OneToOneField
классы, используемые для автоматического превращения в первичный ключ модели. Это больше не так (хотя вы можете передать primary_key
аргумент вручную, если хотите). Таким образом, теперь можно иметь несколько полей типа
OneToOneField
в одной модели.
Модели в файлах ¶
Совершенно нормально связать модель с одной из другого приложения. Для этого импортируйте связанную модель в верхнюю часть файла, в котором определена ваша модель. Затем при необходимости обратитесь к другому классу модели. Например:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Ограничения имени поля ¶
Django накладывает некоторые ограничения на имена полей модели:
Имя поля не может быть зарезервированным словом Python, поскольку это приведет к синтаксической ошибке Python. Например:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
Имя поля не может содержать более одного подчеркивания подряд из-за того, как работает синтаксис поиска запросов Django. Например:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
Имя поля не может заканчиваться знаком подчеркивания по аналогичным причинам.
Однако эти ограничения можно обойти, поскольку имя вашего поля не обязательно должно совпадать с именем столбца базы данных. Смотрите
db_column
вариант.
SQL зарезервированных слов, например join
, where
или select
, которые допускаются в качестве имен полой модели, так как Django ускользает все имена таблиц базы данных и имена столбцов в каждом основном запросе SQL. Он использует синтаксис кавычек вашего конкретного механизма базы данных.
Типы настраиваемых полей ¶
Если одно из существующих полей модели не может использоваться для ваших целей или если вы хотите воспользоваться преимуществами некоторых менее распространенных типов столбцов базы данных, вы можете создать свой собственный класс поля. Полное описание создания собственных полей приведено в разделе «Написание пользовательских полей модели» .
Meta
варианты ¶
Предоставьте метаданные вашей модели с помощью внутреннего , например:class Meta
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
Метаданные модели - это «все, что не является полем», например параметры упорядочения ( ordering
), имя таблицы базы данных ( db_table
) или удобочитаемые имена в единственном и множественном числе ( verbose_name
и
verbose_name_plural
). Ничего не требуется, и добавление к модели совершенно необязательно.class
Meta
Полный список всех возможных Meta
опций можно найти в справочнике опций модели .
Атрибуты модели ¶
objects
- Самый важный атрибут модели - это
Manager
. Это интерфейс, через который операции запросов к базе данных предоставляются моделям Django и используются для извлечения экземпляров из базы данных. Если пользовательскиеManager
настройки не определены, используется имя по умолчаниюobjects
. Менеджеры доступны только через классы модели, но не через экземпляры модели.
Модельные методы ¶
Определите пользовательские методы в модели, чтобы добавить к своим объектам пользовательские функциональные возможности «на уровне строк». В то время как Manager
методы предназначены для работы «в масштабе таблицы», методы модели должны работать с конкретным экземпляром модели.
Это ценный метод для хранения бизнес-логики в одном месте - модели.
Например, в этой модели есть несколько настраиваемых методов:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
Последний метод в этом примере - свойство .
Ссылка экземпляра модели имеет полный список методов автоматически предоставленные для каждой модели . Вы можете переопределить большинство из них - см. Переопределение предопределенных методов модели ниже - но есть пара, которую вы почти всегда захотите определить:
__str__()
«Магический метод» Python, который возвращает строковое представление любого объекта. Это то, что Python и Django будут использовать всякий раз, когда необходимо привести экземпляр модели и отобразить его в виде простой строки. В частности, это происходит, когда вы отображаете объект в интерактивной консоли или в админке.
Вы всегда захотите определить этот метод; значение по умолчанию вообще не очень полезно.
get_absolute_url()
Это сообщает Django, как вычислить URL-адрес объекта. Django использует это в своем интерфейсе администратора, и каждый раз, когда ему нужно выяснить URL-адрес объекта.
Любой объект, имеющий URL-адрес, который однозначно идентифицирует его, должен определять этот метод.
Переопределение предопределенных методов модели ¶
Есть еще один набор методов модели, которые инкапсулируют множество поведений базы данных, которые вы захотите настроить. В частности, вам часто захочется изменить образ save()
и
delete()
работу.
Вы можете переопределить эти методы (и любой другой метод модели), чтобы изменить поведение.
Классический вариант использования для переопределения встроенных методов - это если вы хотите, чтобы что-то происходило при сохранении объекта. Например (см.
save()
Документацию по параметрам, которые он принимает):
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
Вы также можете предотвратить сохранение:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(*args, **kwargs) # Call the "real" save() method.
Важно не забыть вызвать метод суперкласса - это то дело, - чтобы гарантировать, что объект по-прежнему сохраняется в базе данных. Если вы забудете вызвать метод суперкласса, поведения по умолчанию не произойдет, и база данных не будет затронута.super().save(*args, **kwargs)
Также важно передать аргументы, которые можно передать методу модели - это то, что делает бит. Время от времени Django будет расширять возможности встроенных методов модели, добавляя новые аргументы. Если вы используете в определениях своих методов, вы гарантируете, что ваш код будет автоматически поддерживать эти аргументы при их добавлении.*args, **kwargs
*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
в него мета-
класс. Затем эта модель не будет использоваться для создания каких-либо таблиц базы данных. Вместо этого, когда он используется в качестве базового класса для других моделей, его поля будут добавлены к полям дочернего класса.
Пример:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Student
Модель будет иметь три поля: name
, age
и
home_group
. CommonInfo
Модель не может быть использована в качестве нормальной модели Django, поскольку он является абстрактным базовым классом. Он не создает таблицу базы данных, не имеет менеджера и не может быть создан или сохранен напрямую.
Поля, унаследованные от абстрактных базовых классов, могут быть переопределены другим полем или значением или удалены с помощью None
.
Во многих случаях этот тип наследования модели будет именно тем, что вам нужно. Он предоставляет способ вычленить общую информацию на уровне Python, при этом создавая только одну таблицу базы данных для каждой дочерней модели на уровне базы данных.
Meta
наследование ¶
Когда создается абстрактный базовый класс, Django делает любой внутренний класс Meta, который вы объявили в базовом классе, доступным в качестве атрибута. Если дочерний класс не объявляет свой собственный класс Meta , он унаследует родительский класс Meta . Если дочерний элемент хочет расширить родительский класс Meta , он может создать его подкласс. Например:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
Django вносит одну корректировку в класс Meta абстрактного базового класса: перед установкой
атрибута Meta он устанавливается abstract=False
. Это означает, что потомки абстрактных базовых классов не становятся автоматически абстрактными классами. Чтобы создать абстрактный базовый класс, который наследуется от другого абстрактного базового класса, вам необходимо явно указать abstract=True
дочерний класс .
Некоторые атрибуты не имеет смысла включать в класс Meta абстрактного базового класса. Например, включение db_table
будет означать, что все дочерние классы (те, которые не определяют свою собственную Meta ) будут использовать одну и ту же таблицу базы данных, что почти наверняка не то, что вам нужно.
Из-за того, как работает наследование Python, если дочерний класс наследуется от нескольких абстрактных базовых классов, по умолчанию будут наследоваться только параметры Meta из первого перечисленного класса. Чтобы наследовать параметры Meta от нескольких абстрактных базовых классов, вы должны явно объявить наследование Meta . Например:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ['name']
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
Многотабличное наследование ¶
Второй тип наследования моделей, поддерживаемый Django, - это когда каждая модель в иерархии является моделью сама по себе. Каждая модель соответствует своей собственной таблице базы данных, и ее можно запрашивать и создавать индивидуально. Отношения наследования вводят связи между дочерней моделью и каждым из ее родителей (через автоматически созданную OneToOneField
). Например:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
Все поля Place
также будут доступны в Restaurant
, хотя данные будут находиться в другой таблице базы данных. Итак, оба варианта возможны:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
Если у вас есть, Place
который также является Restaurant
, вы можете перейти от
Place
объекта к Restaurant
объекту, используя строчную версию имени модели:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
Однако, если p
в приведенном выше примере не было Restaurant
(он был создан непосредственно как Place
объект или был родительским элементом какого-либо другого класса), ссылка на p.restaurant
вызовет Restaurant.DoesNotExist
исключение.
Автоматически созданный OneToOneField
на
Restaurant
что связывает его Place
выглядит следующим образом :
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Вы можете переопределить это поле, объявив свое собственное
OneToOneField
с помощью parent_link=True
on Restaurant
.
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
атрибуты так же, как обычные модели .
QuerySet
s по-прежнему возвращает запрошенную модель ¶
Невозможно заставить 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 вызовет a, 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
.