Модели

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

Основы:

  • Каждая модель является классом 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 добавлено автоматически, но это поведение может быть переопределено.
  • Синтаксис CREATE TABLE этого примера соответсвует синтаксису PostgreSQL. Следует отменить, что Django использует SQL с учетом серверной части БД, указанной в файле настроек.

Использование моделей

После определения модели необходимо сообщить необходимо сообщить Django, что вы хотите их использовать. Для этого в файле настроек добавьте в настройку INSTALLED_APPS имя модуля, содержащего вашу модель.

Например, если модели Вашего приложения находятся в модуле 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 admin и в автоматисескй сгенерированных формах.

Django поставляется со множеством встроенных типов полей; Вы можете найти полный список полей в справочнике полей модели.

Опции полей

Каждое поле принимает определенный набор специфичных аргументов (задокументированных в справочнике полей модели). Например, CharField (и его субклассы) требуют аргумент max_length, определяющий размер поля VARCHAR, использующийся для хранения данных.

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

null
Если True, Django будет сохранять в базе данных пустое значение как NULL. По умолчанию False.

blank
Если True, то поле может быть пустым. По умолчанию False

Заметьте, что это отличается от null. null отностися чисто к базе данных, тогда как blank относится к валидации. Если поле установлено как blank=True, то при валидации формы будет разрешено пустое значение. Если же поле имеет настройкук blank=False, то заполнение поля будет обязательным

choices
Последовательность двойных кортежов, используемых для поля. Если он задан, то виджет формы по умолчанию будет использовать textbox вместо стандартного поля вводя для ограничения списка выбора.

Список выбора выглядит так:

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'

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 для поля. Если Djnago увидет явно указанный первичный Field.primary_key, то поле id создаваться не будет.

Каждая модель требует обязательного указания одного поля, имеющего primary_key=True (либо объявленного явно, либо добавленного автоматически)

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

Все типы полей, за исключением ForeignKey, ManyToManyField и OneToOneField принимают опциональный первый позиционный аргумент — подробное (verbose) имя. Если подробное имя не задано, Django задаст его, используя атрибут поля name, преобразовав знаки подчеркивания в пробелы.

В этом примере подробным именем является «person’s first name»:

first_name = models.CharField("person's first name", max_length=30)

В этом примере подробным именем будет «first name»

first_name = models.CharField(max_length=30)

Для полей ForeingKey, 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 предлагает способы определния трех наиболее распростарненных типов отношений в БД: один-ко-многим, многие-ко-многим и один-к-одному

Отношение один-ко-многим

Для определения отношения один-ко-многим используйте djang0.db.models.ForeignKey. Используйте его как любой другой тип Field: добавив как атрибут класса Вашей модели.

ForeignKey требует позиционный аргумент: класс, на который ссылается модель.

Например, если модель автомобиля Car имеет производителя (Manufacturer), производитель изготавливает множество авмомобилей, но каждый автомобиль имеет лишь одного производителя то используйте следующее определение:

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

Вы так же можете создать рекурсивное отношение (объект имеет отношении один-ко-многим сама с сабой) и отношение к моделий, которая не определена; смотрите справку по полям модели для подробностей

Рекомендуется, что бы имя внешнего ключа (manufacturer в примере выше) соответствовало имени модели в нижнем регистре. Разумеется, вы можете назвать поле так, как захотите сами. Например:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
    # ...

См. также

поле ForeignKey принимает несколько дополнительных аргументов, описанных в справке по полям моделей. Эти опции помогают определить, как отношения должны работать. Все они опциональны

Подробнее о обратном доступе к связанным объектам см раздел обратных связей

Так же смотрите пример отношения один ко многим

Отношение многие-ко-многим

Для определения отношения многие-ко многим, используйте ManyToManyField. Используйте его, как любой другой тип Field: добавлением как атрибут в модель класса.

Отношение ManyToMany требует позиционного аргумента: Класс, на который ссылается модель

Например, если пица (Pizza) имеет несколько начинок (Topping) то и начинка может быть включена в различные пиццы. Вот как это реализуется:

from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

Как и с ForeignKey, Вы можете так же создать рекурсивное отношение (объект имеет отношение многие-ко-многим сама к себе), и отношение к модели, которая еще не определена

Так же рекомендуется устанавливать имя поля типа ManyToMany (toppings в примере выше) во множественному числе по имени модели на которую создается ссылка.

Не важно, в какой модели будет отношение ManyToMany, но вы должны включить это поле лишь в одну из них — не в обе.

Как правило, поле ManyToMany добавляется в объект, который будет редактироваться в форме. В примере выше toppings включен в Pizza (а не для Topping установлено поле pizzas типа ManyToMany), т.к. это более естественно — пица с начинкой, чем то какие в пиццы могут иметь некоторую начинку. Как сказано выше, форма Pizza позволит пользователю выбрать начинки.

См. также
Пример отношения многие-ко-многим

Поле ManyToMany так же принимает несколько дополнительных аргументов, описаных в справке по полям модели

Дополнительные поля в отношении многие-ко-многим

Если Вы имеете дело с простым отношением многие-ко-многим, таким как пица и ее начинка, то все, что Вам нужно, это ManyToMany. Однако, иногда, Вам может понадобится сопоставить данные в отношении двух моделей.

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

Для этой ситуации Django позволяет определить модель, которая будет использоваться для управления отношением отношением многие-ко-многим. Промежуточная модель ассоциируется с полем ManyToManyField используя аргумент through который указывает на модель, которая выступает в качестве посредника. Для нашего музыкального примера код будет выглядеть примерно так:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Когда Вы определяете промежуточную модель, то явно указываете внешние ключи к моделям, которые учавствуют в отношении многие-ко-многим. Это явное указание определяет, как две моедли связаны.

Есть несколько ограничений на промежуточную модель:

  • Промежуточная модель должна иметь один, и только один внешний ключ на модель — источник (это Group в нашем примере), или Вы должны указать явно внешние ключи, которые Django должен использовать для связи, используя ManyToManyField.through_fields. Если имеется больше одного внешнего ключа, и through_fields не определены, то будет вызвано изсключение. Аналогичное ограничение применяется для внешнего ключа целевой модели. (это Person в нашем примере
  • Для модели, имеющей отношение многие-ко-многим к себе через посредническую модель, допускается два внешних ключа к одной и той же модели, но они будут рассматриваться как две (разные) стороны отношения многие-ко-многим. Однако, если существует более двух внешних ключей, Вы также должны указать through_fields, как указано выше, иначе возникнет ошибка проверки.
  • Когда определяется отношение многие-ко-многим модели к себе, используя промежуточную модель, Вы должны использовать symmetrical=False (см. справку по полям модели)

После того, как вы настроили поле ManyToManyField с использованием промежуточной модели (Memgership в примере), то вы готовы начать создание отношений многие-ко-многим. Делатеся это созданием экземплара промежуточной модели:

>>> 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() для создания отношений, определяя shtough_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)})

Вы можете предпочесть создание промежуточной модели напряму.

Если в промежуточной модели имеются повторения пар связанных моделей, то при вызове 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 распались
>>> 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: включая в атрибут класса Вашей модели.

Это наиболее полезно для первичного ключа, когда необходимо некоторое расширение имеющегося объекта.

Поле OneToOne требует указания позиционного аргумента: класс, на который ссылается модель

Например, если бы вы строили базу данных «мест», вы бы в нее встроили довольно стандартные вещи, такие как адрес, номер телефона и т. д. Затем, если вы хотите построить базу данных ресторанов поверх мест, то вместо того, чтобы повторять себя и копировать эти поля в модели Restaurant, вы можете сделать так, чтобы Restaurant имел OneToOneField к Place (потому что, фактически, ресторан является «местом». Чтобы справиться с этим, вы обычно используете наследование, которое подразумевает неявное отношение один к одному).

Как и в ForeignKey, могут быть определены рекурсивные отношения и ссылки на еще не определенные модели.

см. также
Полный пример использования отношения один-к-одному

Поля OneToOneField так же принимают опциональный аргумент parent_link

Межфайловые модели

Совершенно нормально связывать модели из разных приложений. Для этого импортируйте связываемую модель в начале файла, в котором определена Ваша модель. Затем, просто сошлитесь на необходимый класс модели. Например:

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 reserved word!

2. Имя поля не может содержать более одного подчеркивания подряд, т.к. так работает синтаксис запросов Django

class Example(models.Model):
    foo__bar = models.IntegerField() # 'foo__bar' has two underscores!

3. Имя поля не может заканчиваться знаком подчеркивания, по похожим причинам

Эти ограничения можно обойти, т.к. имя поля класса не обязательно должно совпадать с именем поля таблицы БД. см. опцию db_columnt

Зарезервированные слова SQL, такие как join, where или select, допускаются в качестве имен полей модели, поскольку Django экранирует все имена таблиц базы данных и имена столбцов в каждом базовом запросе SQL. Он использует синтаксис цитирования вашего конкретного движка базы данных

Пользовательские типы полей

Если одно из существующих полей модели нельзя использовать в соответствии с вашими целями или если вы хотите воспользоваться некоторыми менее распространенными типами столбцов базы данных, вы можете создать свой собственный класс полей. Полный охват создания собственных полей приведен в разделе «Создание пользовательских полей модели».

Передайте метаданные Вашей модели, используя внутренний класс 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). Класс Meta не обязателен, и может быть добавлен опционально.

Полный список возможных опций Meta можно найти в справке по опциям модели

Атрибуты модели

objects
Наиболее важный атрибут модели — это Manager. Это интерфейс, с помощью которого операции запроса к базе данных предоставляются моделям Django и используется для извлечения экземпляров из базы данных. Менеджер доступен только из класса модели. В экземпляре объекта он не доступен.

Методы модели

Определите пользовательские методы на модели для добавления функциональности уровня строки для Вашего объекта. В то время, как методы мендежера предназначены для выполнения «табличных» задач, методы модели ориентированы на конкретный экземпляр модели.

Это ценная техника для хранения бизнес-логики в одноме месте — модели.

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().

Вы можете переопределить эти методы (и любой другой метод модели), чтобы изменить поведение.

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) — чтобы гарантировать, что объект сохраняется в базе данных. Если вы забудете вызвать метод суперкласса, поведение по умолчанию не произойдет, и база данных затронута не будет.

Переопределенные методы модели не вызываются в массовых операциях

Обратите внимание, что метод delete() для объекта не обязательно вызывается при массовом удалении объектов с использованием QuerySet или в результате каскадного удаления. Чтобы обеспечить выполнение настроенной логики удаления, вы можете использовать сигналы pre_delete и / или post_delete.

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

Обсуждение закрыто.