Модели

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

Основы :

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

  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

Вы можете назначить метаданные своей модели с помощью 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 возможно три типа наследования.

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

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

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

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

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

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

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

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

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

Справочник моделей
Документирует все API-интерфейсы, связанные с моделями, включая поля модели, связанные объекты и запросы ( QuerySet ).

Copyright ©2020 All rights reserved