Модели

Модель - это единственный исчерпывающий источник информации о ваших данных. Он содержит основные поля и поведение данных, которые вы храните. Как правило, каждая модель отображается в одну таблицу базы данных.

Основы:

  • Каждая модель представляет собой подклассы 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 startappINSTALLED_APPS

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

Когда вы добавляете новые приложения INSTALLED_APPS, обязательно запустите их , при желании сначала сделав для них миграции с .manage.py migratemanage.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(либо явно объявленного, либо автоматически добавленного).

Изменено в Django 3.2:

В более старых версиях автоматически создаваемые поля первичного ключа всегда были AutoFields.

Подробные имена полей

Каждый тип поля, за исключением 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 накладывает некоторые ограничения на имена полей модели:

  1. Имя поля не может быть зарезервированным словом Python, поскольку это приведет к синтаксической ошибке Python. Например:

    class Example(models.Model):
        pass = models.IntegerField() # 'pass' is a reserved word!
    
  2. Имя поля не может содержать более одного подчеркивания подряд из-за того, как работает синтаксис поиска запросов Django. Например:

    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
    
  3. Имя поля не может заканчиваться знаком подчеркивания по аналогичным причинам.

Однако эти ограничения можно обойти, поскольку имя вашего поля не обязательно должно совпадать с именем столбца базы данных. Смотрите 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 deletepre_deletepost_delete

К сожалению, не существует обходной путь , когда creatingили updatingобъекты в объеме, так как ни один из save(), pre_saveи post_saveназываются.

Выполнение собственного SQL

Другой распространенный шаблон - написание пользовательских операторов SQL в методах модели и методах уровня модуля. Дополнительные сведения об использовании необработанного SQL см. В документации по использованию необработанного SQL .

Наследование модели

Наследование модели в Django работает почти так же, как обычное наследование классов в Python, но все же следует соблюдать основы, изложенные в начале страницы. Это означает, что базовый класс должен быть подклассом django.db.models.Model.

Единственное решение, которое вам нужно принять, - это то, хотите ли вы, чтобы родительские модели были моделями сами по себе (со своими собственными таблицами базы данных), или если родители являются просто держателями общей информации, которая будет видна только через дочерние модели.

В Django возможны три стиля наследования.

  1. Часто вы просто хотите использовать родительский класс для хранения информации, которую вы не хотите вводить для каждой дочерней модели. Этот класс никогда не будет использоваться изолированно, поэтому вам нужны абстрактные базовые классы .
  2. Если вы подклассифицируете существующую модель (возможно, что-то целиком из другого приложения) и хотите, чтобы каждая модель имела свою собственную таблицу базы данных, вам подойдет многотабличное наследование .
  3. Наконец, если вы хотите только изменить поведение модели на уровне 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=Trueon 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')

Прокси-модели

При использовании наследования нескольких таблиц новая таблица базы данных создается для каждого подкласса модели. Обычно это желаемое поведение, поскольку подклассу нужно место для хранения любых дополнительных полей данных, которые отсутствуют в базовом классе. Однако иногда вам нужно только изменить поведение модели на языке Python - например, изменить диспетчер по умолчанию или добавить новый метод.

Для этого и используется наследование прокси-модели: создание прокси для исходной модели. Вы можете создавать, удалять и обновлять экземпляры прокси-модели, и все данные будут сохранены, как если бы вы использовали исходную (без прокси) модель. Разница в том, что вы можете изменить такие вещи, как порядок модели по умолчанию или менеджер по умолчанию в прокси-сервере, без изменения оригинала.

Прокси-модели объявлены как обычные модели. Вы сообщаете Django, что это прокси-модель, устанавливая для proxyатрибута Metaкласса значение True.

Например, предположим, что вы хотите добавить метод к Personмодели. Сделать это можно так:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPersonКласс работает на одной и той же таблицы базы данных в качестве родительского Personкласса. В частности, любые новые экземпляры Personтакже будут доступны через MyPersonи наоборот:

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

Вы также можете использовать прокси-модель для определения другого порядка по умолчанию в модели. Возможно, вы не всегда хотите заказывать Personмодель, но регулярно упорядочивайте ее по last_nameатрибуту при использовании прокси:

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

Теперь обычные Personзапросы будут неупорядоченными, а OrderedPersonзапросы будут упорядочены по last_name.

Прокси-модели наследуют Metaатрибуты так же, как обычные модели .

QuerySets по-прежнему возвращает запрошенную модель

Невозможно заставить Django возвращать, скажем, MyPersonобъект всякий раз, когда вы запрашиваете Personобъекты. Набор запросов для Personобъектов вернет эти типы объектов. Весь смысл прокси-объектов заключается в том, что код, основанный на оригинале, Personбудет использовать их, а ваш собственный код может использовать включенные вами расширения (на которые в любом случае не полагается ни один другой код). Это не способ Personповсюду заменять (или любую другую) модель чем-то созданным вами.

Ограничения базового класса

Прокси-модель должна наследовать ровно от одного неабстрактного класса модели. Вы не можете наследовать от нескольких неабстрактных моделей, поскольку прокси-модель не обеспечивает связи между строками в разных таблицах базы данных. Прокси-модель может наследовать от любого количества классов абстрактных моделей, если они не определяют никаких полей модели. Прокси-модель также может наследовать от любого количества прокси-моделей, которые имеют общий неабстрактный родительский класс.

Менеджеры прокси-моделей

Если вы не укажете менеджеров модели в прокси-модели, она наследует менеджеров от своей родительской модели. Если вы определите менеджера в модели прокси, он станет менеджером по умолчанию, хотя все менеджеры, определенные в родительских классах, по-прежнему будут доступны.

Продолжая наш пример выше, вы можете изменить менеджер по умолчанию, используемый при запросе Personмодели следующим образом:

from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

Если вы хотите добавить новый менеджер к прокси, не заменяя существующий по умолчанию, вы можете использовать методы, описанные в документации настраиваемого менеджера : создать базовый класс, содержащий новых менеджеров, и унаследовать его после основного базового класса:

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True

Вероятно, вам не придется делать это очень часто, но когда вы это сделаете, это возможно.

Различия между наследованием прокси и неуправляемыми моделями

Наследование прокси-модели может выглядеть довольно похоже на создание неуправляемой модели с использованием managedатрибута в Metaклассе модели .

При тщательной настройке Meta.db_tableвы можете создать неуправляемую модель, которая затеняет существующую модель и добавляет к ней методы Python. Однако это было бы очень повторяющимся и хрупким, поскольку вам необходимо синхронизировать обе копии, если вы внесете какие-либо изменения.

С другой стороны, прокси-модели предназначены для того, чтобы вести себя точно так же, как модель, для которой они проксируются. Они всегда синхронизированы с родительской моделью, поскольку напрямую наследуют ее поля и менеджеров.

Общие правила таковы:

  1. Если вы зеркалируете существующую модель или таблицу базы данных и не хотите, чтобы все столбцы исходной таблицы базы данных, используйте Meta.managed=False. Эта опция обычно полезна для моделирования представлений базы данных и таблиц, не находящихся под контролем Django.
  2. Если вы хотите изменить поведение модели только для 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 startappmodels.py

Для этого создайте modelsпакет. Удалите models.pyи создайте myapp/models/каталог с __init__.pyфайлом и файлами для хранения ваших моделей. Вы должны импортировать модели в __init__.pyфайл.

Например, если у вас были organic.pyи synthetic.pyв models каталоге:

myapp / models / __ init__.py
from .organic import Person
from .synthetic import Robot

Явный импорт каждой модели вместо использования имеет преимущества в том, что он не загромождает пространство имен, делает код более читаемым и сохраняет полезными инструменты анализа кода.from .models import *

Смотрите также

Справочник по моделям
Охватывает все API, связанные с моделью, включая поля модели, связанные объекты и QuerySet.

Copyright ©2021 All rights reserved