Модели ¶
Модель - это единственный и окончательный источник информации о ваших данных. Он содержит основные поля и поведение хранимых вами данных. Обычно каждой модели соответствует одна таблица в базе данных.
Основы :
- Каждая модель - это класс 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 в этом примере отформатирован с использованием синтаксиса 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
.Первый элемент каждого кортежа - это значение, которое будет храниться в базе данных. Второй элемент отображается компонентом поля формы.
Учитывая экземпляр модели, мы можем получить доступ к отображаемому значению поля выбора с помощью метода
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 добавляет в каждый шаблон следующее поле:
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
принимают несколько дополнительных параметров, которые показаны в справочнике по полям шаблона . Эти параметры помогают указать, как работают отношения; все необязательны.
Дополнительные сведения о доступе к объектам по обратной связанной ссылке см. В примере доступа к обратным отношениям .
Примеры кода см. В разделе Примеры взаимосвязей моделей "многие к одному" .
Отношения многие-ко-многим ¶
Чтобы определить отношение «многие ко многим», используйте django.db.models.ManyToManyField
. Его использование такое же, как и у других типов элементов управления Field
: это атрибут класса модели.
ManyToManyField
требуется позиционный параметр: класс, с которым связана модель.
Например, если у одного Pizza
есть несколько объектов Topping
(топпинг), то есть один Topping
может быть на нескольких пиццах, и у каждого Pizza
есть несколько начинок, это будет представлено следующим образом:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
Что касается ForeignKey
, также возможно создавать рекурсивные отношения (объект с отношением «многие ко многим» с самим собой) и отношения с неопределенными моделями .
Рекомендуется, но не обязательно, чтобы имя поля ManyToManyField
( toppings
в приведенном выше примере) было множественным числом, описывающим все связанные объекты модели.
То ManyToManyField
, помещается ли поле в одну или другую модель, не сильно меняет, но важно помещать его только в одну из моделей, а не в обе.
Обычно экземпляры ManyToManyField
следует помещать в объект, который будет изменен формой. В приведенном выше примере, toppings
в Pizza
(а не Topping
имеющем поле ManyToManyField
в pizzas
) , потому что это имеет смысл представить пиццу , имеющее множество начинки , чем долив , который находится в несколько пице. Форма, как было определено выше, Pizza
позволит выбрать начинки.
Смотрите также
См. Примеры отношений «многие ко многим» между моделями, чтобы получить полный пример.
Поля ManyToManyField
также принимают некоторые дополнительные параметры, которые показаны в справочнике по полям шаблона . Эти параметры помогают указать, как работают отношения; все необязательны.
Дополнительные поля в отношениях "многие ко многим" ¶
Когда вам приходится иметь дело с отношениями `` многие ко многим '', такими как смешивание и комбинирование пиццы и начинки, достаточно одного ManyToManyField
стандарта. Однако иногда необходимо связать данные с отношениями между двумя моделями.
Например, рассмотрим случай приложения, связывающего музыкантов и музыкальные группы, к которым они принадлежат. Между человеком и группами, членами которых он является, существует связь «многие ко многим», поэтому ManyToManyField
для представления этой связи можно использовать поле . Однако есть много деталей о членстве в группе, которые, возможно, стоит сохранить, например, когда человек присоединился к группе.
В таких ситуациях Django позволяет указать шаблон, который будет использоваться для определения отношения «многие ко многим». Затем можно определить дополнительные поля в промежуточной модели. Последний связан с полем ManyToManyField
с помощью параметра, through
который будет указывать на модель, действующую в качестве посредника. В нашем музыкальном примере код может выглядеть так:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
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
требуется позиционный параметр: класс, с которым связана модель.
Например, если бы вы Place
создали базу данных «местоположений» ( в примере), это потребовало бы установки довольно стандартных элементов, таких как адрес, номер телефона и т. Д. в базе данных. Затем, если вы хотите создать базу данных ресторанов поверх базовых сайтов, вместо того, чтобы повторяться и реплицировать все поля в модели 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
Вы можете назначить метаданные своей модели с помощью 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)
Последний метод в этом примере - это свойство .
Модель Инстансы Reference предоставляет полный список методов автоматически наследуется каждой модели . Вы можете переопределить большинство из них, см. Ниже перегрузку предопределенных методов шаблона , но вы почти всегда захотите определить некоторые из них в своих шаблонах:
__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 или с помощью . Чтобы убедиться, что пользовательская логика подавления выполняется, вы можете использовать сигналы или .suppression en cascade
pre_delete
post_delete
К сожалению, при загрузке création
или выгрузке незакрепленных объектов обходного пути нет , поскольку ни , ни , ни вызываются.mise à jour
save()
pre_save
post_save
Запуск собственного SQL ¶
Еще одно довольно распространенное использование - написание пользовательских команд SQL в методах модели и методах уровня модуля. Подробнее об использовании необработанного SQL см. В документации по использованию необработанного SQL .
Наследование модели ¶
В Django наследование модели работает почти так же, как наследование классов, как это сделано в Python, но основы, показанные в начале этой страницы, по-прежнему актуальны. Это означает, что базовый класс должен быть подклассом django.db.models.Model
.
Единственное решение, которое вы примете, - хотите ли вы, чтобы родительские модели были полноценными моделями (со своей собственной таблицей базы данных) или если родительские модели были просто контейнерами общей информации, которая будет только видимой. 'через их дочерние модели.
В Django возможно три типа наследования.
- Часто вам просто нужно, чтобы родительский класс содержал информацию, которую вы не хотите повторно вводить в каждую дочернюю модель. Этот класс никогда не будет использоваться сам по себе, поэтому это абстрактные базовые классы .
- Если вы наследуете существующую модель (возможно, совсем от другого приложения) и хотите, чтобы каждая модель имела свою собственную таблицу базы данных, это наследование с несколькими таблицами .
- Наконец, если вы хотите изменить поведение модели только в ее коде Python, не касаясь полей модели, это прокси-модели .
Абстрактные базовые классы ¶
Абстрактные базовые классы полезны, когда вы хотите сгруппировать некоторую информацию, общую для набора моделей. Вы пишете базовый класс и указываете abstract=True
в его классе Meta . Для этой модели не будет создана таблица базы данных. Однако, когда он будет использоваться в качестве базового класса для других моделей, его поля будут добавлены к полям дочернего класса.
Пример :
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Модель Student
будет иметь три поля: name
, age
и home_group
. Модель CommonInfo
нельзя использовать как обычную модель Django, потому что это абстрактный базовый класс. Эта модель не генерирует таблицу базы данных и не имеет менеджера; он не может быть создан или сохранен напрямую.
Поля, унаследованные от абстрактных базовых классов, можно заменить другим полем или значением или даже удалить, задав для имени поля значение None
.
Во многих ситуациях уместен именно этот тип наследования модели. Он предлагает возможность группировки общей информации на уровне Python при создании только одной таблицы базы данных для каждой дочерней модели.
Наследование от Meta
¶
Когда создается абстрактный базовый класс, Django делает доступным любой вложенный мета- класс, объявленный в базовом классе, в качестве атрибута. Если дочерний класс не объявляет свой собственный класс Meta , он наследует класс Meta от своего родителя. Если дочерний класс хочет расширить родительский класс Meta , он может унаследовать его. Например :
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
, вы можете получить доступ к объекту Restaurant
изнутри объекта, Place
используя имя модели в нижнем регистре:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
Однако, если в приведенном выше примере p
было неRestaurant
(либо потому , что он был создан непосредственно как объект Place
или потому , что она является родителем другого класса), доступ к p.restaurant
бросили бы исключение Restaurant.DoesNotExist
,
OneToOneField
Автоматически созданное поле, к Restaurant
которому он привязан, Place
выглядит так:
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Это поле можно переопределить, объявив свое собственное OneToOneField
с помощью parent_link=True
в классе Restaurant
.
Meta
и многотабличное наследование ¶
В случае многотабличного наследования, наследование от родительского класса Meta не имеет отношения к дочернему классу. Все параметры Meta уже были применены к родительскому классу, и новое приложение приведет только к противоречивому поведению (в отличие от случая абстрактного базового класса, где базовый класс не существует для себя) ,
Таким образом, дочерняя модель не имеет доступа к мета- классу своего родителя. Однако есть несколько ограниченных случаев, когда дочерний элемент наследует поведение от своего родителя: если дочерний элемент не указывает атрибут 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
для атрибута class 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
всегда возвращают запрошенную модель ¶
Невозможно MyPerson
указать Django возвращать объект, например, каждый раз , когда вы запрашиваете объекты 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 , будет использоваться только первое вхождение, а все остальные будут проигнорированы.
Как правило, нет необходимости в наследовании от нескольких родителей. Основная причина для этого - в случае «смешанных» классов: добавление определенного дополнительного поля или метода в каждый класс, унаследованный от «смешанного» класса. Старайтесь, чтобы ваша иерархия наследования была как можно более простой и понятной, чтобы вам не приходилось изо всех сил искать источник конкретной информации или поведения.
Обратите внимание, что наследование от нескольких моделей, которые имеют 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 это обычно не разрешено для полей модели. Если неабстрактный базовый класс модели имеет поле auteur
, невозможно создать другое поле модели или установить именованный атрибут auteur
в классах, которые наследуются от этого базового класса.
Это ограничение не распространяется на поля модели, унаследованные от абстрактной модели. Такие поля можно заменить другим полем или значением или даже удалить путем установки .nom_de_champ = None
Предупреждение
Менеджеры моделей унаследованы от абстрактных базовых классов. Перегрузка устаревшего поля, на которое ссылается устаревшее поле, Manager
может вызвать небольшие аномалии. См. Настраиваемые менеджеры и наследование модели .
Заметка
Некоторые поля определяют дополнительные атрибуты моделей, например. ключ ForeignKey
определяет дополнительный атрибут, состоящий из имени поля, добавленного к суффиксу _id
, а также значений related_name
и related_query_name
связанной модели.
Эти дополнительные атрибуты нельзя заменить без изменения или удаления поля, в котором они определены, так, чтобы оно больше не определяло эти атрибуты.
Перегрузка полей родительской модели вызывает проблемы в областях инициализации новых экземпляров (чтобы указать, какое поле инициализируется Model.__init__
) и сериализации. Это ситуации, которые не влияют на наследование обычных классов Python таким же образом, поэтому это различие между наследованием классов Python и наследованием моделей Django не является произвольным.
Это ограничение применяется только к атрибутам, являющимся экземплярами Field
. Обычные атрибуты Python можно без проблем перегрузить. Это также применимо только к имени атрибута, как его видит Python: если вы вручную укажете имя столбца базы данных, возможно, что одно и то же имя столбца появится в обоих дочерняя и его родительская модели в многотабличном наследовании (в конечном итоге это столбцы, принадлежащие двум разным таблицам базы данных).
Django сообщает об ошибке, FieldError
если вы переопределяете поле шаблона родительского класса.
Организация моделей в пакеты ¶
Команда создает структуру приложения, которая включает файл . Если у вас много шаблонов, может быть полезно организовать их в отдельные файлы.manage.py startapp
models.py
Для этого создайте пакет models
. Удалите models.py
и создайте каталог, mon_app/models/
содержащий файл, а __init__.py
также файлы, в которых хранятся модели. Вам необходимо импортировать модели в файл __init__.py
.
Например, если у вас есть organic.py
и synthetic.py
в каталоге models
:
from .organic import Person
from .synthetic import Robot
Явный импорт каждой модели, а не массовый, имеет преимущества, заключающиеся в том, что он не загрязняет пространство имен, делает код более читаемым и упрощает работу инструментов анализа кода.from .models import *
Смотрите также
- Справочник моделей
- Документирует все API-интерфейсы, связанные с моделями, включая поля модели, связанные объекты и запросы (
QuerySet
).