Spec-Zone.ru › Django 6.0

Справочник по экземплярам моделей

В этом документе подробно описан API Model. Он основан на материалах руководств по моделям и запросам к базе данных, поэтому, прежде чем читать этот документ, рекомендуется ознакомиться с ними и понять их содержание.

В этом справочнике мы будем использовать пример моделей блога, представленный в руководстве по запросам к базе данных.

Создание объектов

Чтобы создать новый экземпляр модели, создайте его так же, как экземпляр любого другого класса Python:

class Model(**kwargs) [исходный код]

Именами ключевых аргументов являются имена полей, определённых в вашей модели. Обратите внимание, что создание экземпляра модели никак не затрагивает базу данных; для этого нужно вызвать save().

Примечание

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

  1. Добавьте метод класса в класс модели:

    from django.db import models
    
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # do something with the book
            return book
    
    
    book = Book.create("Pride and Prejudice")
    
  2. Добавьте метод в пользовательский менеджер (обычно это предпочтительный вариант):

    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    
    book = Book.objects.create_book("Pride and Prejudice")
    

Настройка загрузки моделей

classmethod Model.from_db(db, field_names, values) [исходный код]

Метод from_db() можно использовать для настройки создания экземпляров модели при загрузке из базы данных.

Аргумент db содержит псевдоним базы данных, из которой загружается модель, field_names содержит имена всех загруженных полей, а values — загруженные значения каждого поля в field_names. Значения field_names расположены в том же порядке, что и values. Если присутствуют все поля модели, то гарантируется, что values расположены в порядке, ожидаемом __init__(). То есть экземпляр можно создать с помощью cls(*values). Если некоторые поля отложены, они не появятся в field_names. В этом случае присвойте каждому отсутствующему полю значение django.db.models.DEFERRED.

Помимо создания новой модели, метод from_db() должен установить флаги adding и db в атрибуте _state нового экземпляра.

Ниже приведён пример записи исходных значений полей, загруженных из базы данных:

from django.db.models import DEFERRED


@classmethod
def from_db(cls, db, field_names, values):
    # Default implementation of from_db() (subject to change and could
    # be replaced with super()).
    if len(values) != len(cls._meta.concrete_fields):
        values = list(values)
        values.reverse()
        values = [
            values.pop() if f.attname in field_names else DEFERRED
            for f in cls._meta.concrete_fields
        ]
    instance = cls(*values)
    instance._state.adding = False
    instance._state.db = db
    # customization to store the original field values on the instance
    instance._loaded_values = dict(
        zip(field_names, (value for value in values if value is not DEFERRED))
    )
    return instance


def save(self, **kwargs):
    # Check how the current values differ from ._loaded_values. For example,
    # prevent changing the creator_id of the model. (This example doesn't
    # support cases where 'creator_id' is deferred).
    if not self._state.adding and (
        self.creator_id != self._loaded_values["creator_id"]
    ):
        raise ValueError("Updating the value of creator isn't allowed")
    super().save(**kwargs)

В примере выше показана полная реализация from_db(), чтобы пояснить, как это делается. В данном случае в методе from_db() можно было бы использовать вызов super().

Обновление объектов из базы данных

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

>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field  # Loads the field from the database
Model.refresh_from_db(using=None, fields=None, from_queryset=None) [исходный код]
Model.arefresh_from_db(using=None, fields=None, from_queryset=None)

Асинхронная версия: arefresh_from_db()

Если вам нужно повторно загрузить значения модели из базы данных, используйте метод refresh_from_db(). При вызове этого метода без аргументов выполняются следующие действия:

  1. Все незадержанные поля модели обновляются значениями, которые в данный момент находятся в базе данных.
  2. Все кэшированные связи удаляются из обновлённого экземпляра.

Из базы данных повторно загружаются только поля модели. Другие зависящие от базы данных значения, например аннотации, не загружаются повторно. Атрибуты @cached_property также не очищаются.

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

С помощью аргумента fields можно принудительно указать набор загружаемых полей.

Например, чтобы проверить, что вызов update() привёл к ожидаемому обновлению, можно написать тест, похожий на этот:

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F("val") + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)

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

class ExampleModel(models.Model):
    def refresh_from_db(self, using=None, fields=None, **kwargs):
        # fields contains the name of the deferred field to be
        # loaded.
        if fields is not None:
            fields = set(fields)
            deferred_fields = self.get_deferred_fields()
            # If any deferred field is going to be loaded
            if fields.intersection(deferred_fields):
                # then load all of them
                fields = fields.union(deferred_fields)
        super().refresh_from_db(using, fields, **kwargs)

Аргумент from_queryset позволяет использовать набор запросов, отличный от созданного на основе _base_manager. Это даёт больше контроля над повторной загрузкой модели. Например, если в модели используется мягкое удаление, можно настроить refresh_from_db() с учётом этого:

obj.refresh_from_db(from_queryset=MyModel.active_objects.all())

Можно кэшировать связанные объекты, которые иначе были бы удалены из обновлённого экземпляра:

obj.refresh_from_db(from_queryset=MyModel.objects.select_related("related_field"))

Перед повторной загрузкой значений модели можно заблокировать строку до конца транзакции:

obj.refresh_from_db(from_queryset=MyModel.objects.select_for_update())
Model.get_deferred_fields() [исходный код]

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

Проверка объектов

Проверка модели состоит из четырёх этапов:

  1. Проверка полей модели — Model.clean_fields()
  2. Проверка модели в целом — Model.clean()
  3. Проверка уникальности полей — Model.validate_unique()
  4. Проверка ограничений — Model.validate_constraints()

Все четыре этапа выполняются при вызове метода full_clean() модели.

При использовании ModelForm вызов is_valid() выполнит эти этапы проверки для всех полей, включённых в форму. Дополнительную информацию см. в документации по ModelForm. Вызывать метод full_clean() модели нужно только в том случае, если вы собираетесь самостоятельно обрабатывать ошибки проверки или исключили из ModelForm поля, которые требуют проверки.

Model.full_clean(exclude=None, validate_unique=True, validate_constraints=True) [исходный код]

Этот метод вызывает по порядку Model.clean_fields(), Model.clean(), Model.validate_unique() (если validate_unique имеет значение True) и Model.validate_constraints() (если validate_constraints имеет значение True), а затем вызывает исключение ValidationError с атрибутом message_dict, содержащим ошибки всех четырёх этапов.

Необязательный аргумент exclude позволяет передать set имён полей, которые следует исключить из проверки и очистки. ModelForm использует этот аргумент, чтобы исключить из проверки поля, отсутствующие в форме, поскольку пользователь не смог бы исправить возникшие ошибки.

Обратите внимание: full_clean() не вызывается автоматически при вызове метода save() модели. Если вам нужно выполнить одноэтапную проверку модели, созданной вручную, вызовите его самостоятельно. Например:

from django.core.exceptions import ValidationError

try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass

На первом этапе full_clean() очищает каждое поле по отдельности.

Model.clean_fields(exclude=None) [исходный код]

Этот метод проверяет все поля модели. Необязательный аргумент exclude позволяет передать set имён полей, которые следует исключить из проверки. Если проверка каких-либо полей завершится неудачей, будет вызвано исключение ValidationError.

На втором этапе full_clean() вызывает Model.clean(). Этот метод следует переопределить, чтобы выполнять пользовательскую проверку модели.

Model.clean() [исходный код]

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

import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _


class Article(models.Model):
    ...

    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == "draft" and self.pub_date is not None:
            raise ValidationError(_("Draft entries may not have a publication date."))
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == "published" and self.pub_date is None:
            self.pub_date = datetime.date.today()

Однако обратите внимание: как и Model.full_clean(), метод clean() модели не вызывается при вызове метода save().

В примере выше исключение ValidationError, вызванное методом Model.clean(), было создано со строкой, поэтому оно будет сохранено в специальном ключе словаря ошибок — NON_FIELD_ERRORS. Этот ключ используется для ошибок, относящихся ко всей модели, а не к конкретному полю:

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

Чтобы связать исключения с конкретным полем, создайте экземпляр ValidationError, передав словарь, ключами которого являются имена полей. Предыдущий пример можно изменить так, чтобы ошибка относилась к полю pub_date:

class Article(models.Model):
    ...

    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == "draft" and self.pub_date is not None:
            raise ValidationError(
                {"pub_date": _("Draft entries may not have a publication date.")}
            )
        ...

Если во время Model.clean() обнаружены ошибки в нескольких полях, можно также передать словарь, сопоставляющий имена полей с ошибками:

raise ValidationError(
    {
        "title": ValidationError(_("Missing title."), code="required"),
        "pub_date": ValidationError(_("Invalid date."), code="invalid"),
    }
)

Затем full_clean() проверит ограничения уникальности модели.

Как вызывать ошибки проверки конкретных полей, если эти поля отсутствуют в ModelForm

Нельзя вызывать ошибки проверки в Model.clean() для полей, отсутствующих в форме модели (форма может ограничивать набор полей с помощью Meta.fields или Meta.exclude). Это приведёт к вызову ValueError, поскольку ошибку проверки невозможно будет связать с исключённым полем.

Чтобы решить эту проблему, переопределите Model.clean_fields(), поскольку он получает список полей, исключённых из проверки. Например:

class Article(models.Model):
    ...

    def clean_fields(self, exclude=None):
        super().clean_fields(exclude=exclude)
        if self.status == "draft" and self.pub_date is not None:
            if exclude and "status" in exclude:
                raise ValidationError(
                    _("Draft entries may not have a publication date.")
                )
            else:
                raise ValidationError(
                    {
                        "status": _(
                            "Set status to draft if there is not a publication date."
                        ),
                    }
                )
Model.validate_unique(exclude=None) [исходный код]

Этот метод похож на clean_fields(), но проверяет ограничения уникальности, заданные с помощью Field.unique, Field.unique_for_date, Field.unique_for_month, Field.unique_for_year или Meta.unique_together модели, а не значения отдельных полей. Необязательный аргумент exclude позволяет передать set имён полей, которые следует исключить из проверки. Если проверка каких-либо полей завершится неудачей, будет вызвано исключение ValidationError.

Объекты UniqueConstraint, определённые в Meta.constraints, проверяются методом Model.validate_constraints().

Обратите внимание: если передать аргумент exclude в validate_unique(), ограничение unique_together, включающее одно из указанных полей, проверяться не будет.

Наконец, full_clean() проверит все остальные ограничения модели.

Model.validate_constraints(exclude=None) [исходный код]

Этот метод проверяет все ограничения, определённые в Meta.constraints. Необязательный аргумент exclude позволяет передать set имён полей, которые следует исключить из проверки. Если проверка каких-либо ограничений завершится неудачей, будет вызвано исключение ValidationError.

Сохранение объектов

Чтобы сохранить объект обратно в базу данных, вызовите save():

Model.save(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None) [источник]
Model.asave(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)

Асинхронная версия: asave()

Подробные сведения об использовании аргументов force_insert и force_update см. в разделе Принудительное выполнение INSERT или UPDATE. Сведения об аргументе update_fields приведены в разделе Указание полей для сохранения.

Если вы хотите изменить поведение сохранения, переопределите этот метод save(). Подробнее см. в разделе Переопределение предопределённых методов модели.

У процесса сохранения модели есть и другие особенности; см. разделы ниже.

Автоинкрементные первичные ключи

Если у модели есть AutoField — автоинкрементный первичный ключ, — то при первом вызове save() это автоинкрементное значение будет вычислено и сохранено как атрибут объекта:

>>> b2 = Blog(name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b2.id  # Returns None, because b2 doesn't have an ID yet.
>>> b2.save()
>>> b2.id  # Returns the ID of your new object.

Невозможно узнать значение ID до вызова save(), поскольку это значение вычисляется базой данных, а не Django.

Для удобства у каждой модели по умолчанию есть поле AutoField с именем id, если только вы явно не укажете primary_key=True для поля модели. Подробнее см. документацию по AutoField.

Свойство pk

Model.pk

Независимо от того, определяете ли вы поле первичного ключа самостоятельно или позволяете Django создать его, у каждой модели будет свойство с именем pk. Оно ведёт себя как обычный атрибут модели, но фактически является псевдонимом поля или полей, составляющих первичный ключ модели. Вы можете читать и задавать это значение, как и любое другое значение атрибута; при этом будут обновлены соответствующие поля модели.

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

Добавлена поддержка первичного ключа, состоящего из нескольких полей, с помощью CompositePrimaryKey.

Явное указание значений автоинкрементного первичного ключа

Если у модели есть AutoField, но вы хотите явно указать ID нового объекта при сохранении, задайте его до сохранения, а не полагайтесь на автоматическое присваивание ID:

>>> b3 = Blog(id=3, name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b3.id  # Returns 3.
>>> b3.save()
>>> b3.id  # Returns 3.

Если вы вручную задаёте значения автоинкрементного первичного ключа, убедитесь, что не используете уже существующее значение первичного ключа! Если вы создадите новый объект с явно указанным значением первичного ключа, которое уже есть в базе данных, Django решит, что вы изменяете существующую запись, а не создаёте новую.

В приведённом выше примере блога 'Cheddar Talk' этот код перезапишет предыдущую запись в базе данных:

b4 = Blog(id=3, name="Not Cheddar", tagline="Anything but cheese.")
b4.save()  # Overrides the previous blog with ID=3!

Причину этого поведения см. ниже в разделе Как Django выбирает между UPDATE и INSERT.

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

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

Что происходит при сохранении?

При сохранении объекта Django выполняет следующие действия:

  1. Отправляет сигнал pre-save. Отправляется сигнал pre_save, что позволяет функциям, ожидающим этот сигнал, выполнить необходимые действия.
  2. Предварительно обрабатывает данные. Для каждого поля вызывается метод pre_save(), который выполняет необходимые автоматические изменения данных. Например, поля даты и времени переопределяют pre_save() для реализации auto_now_add и auto_now.
  3. Подготавливает данные для базы данных. Для каждого поля вызывается метод get_db_prep_save(), чтобы получить текущее значение в типе данных, который можно записать в базу данных.

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

    Например, поля DateField используют для хранения данных объект Python datetime. Базы данных не хранят объекты datetime, поэтому перед вставкой в базу данных значение поля необходимо преобразовать в строку даты, соответствующую стандарту ISO.

  4. Вставляет данные в базу данных. Предварительно обработанные и подготовленные данные включаются в SQL-запрос для вставки в базу данных.
  5. Отправляет сигнал post-save. Отправляется сигнал post_save, что позволяет функциям, ожидающим этот сигнал, выполнить необходимые действия.

Как Django выбирает между UPDATE и INSERT

Возможно, вы заметили, что объекты базы данных Django используют один и тот же метод save() для создания и изменения объектов. Django избавляет от необходимости использовать SQL-операторы INSERT или UPDATE. В частности, при вызове save(), если атрибут первичного ключа объекта не определяет значение default или db_default, Django использует следующий алгоритм:

  • Если атрибут первичного ключа объекта имеет любое значение, кроме None, Django выполняет UPDATE.
  • Если атрибут первичного ключа объекта не задан или UPDATE ничего не обновил (например, если первичному ключу присвоено значение, отсутствующее в базе данных), Django выполняет INSERT.

Если для атрибута первичного ключа объекта задано значение default или db_default, Django выполняет UPDATE, если это существующий экземпляр модели и первичный ключ имеет значение, присутствующее в базе данных. В противном случае Django выполняет INSERT.

Здесь важно не указывать явно значение первичного ключа при сохранении новых объектов, если вы не можете гарантировать, что оно ещё не используется. Подробнее об этом нюансе см. выше в разделе Явное указание значений автоинкрементного первичного ключа и ниже в разделе Принудительное выполнение INSERT или UPDATE.

В Django 1.5 и более ранних версиях Django выполнял SELECT, если атрибут первичного ключа был задан. Если SELECT находил строку, Django выполнял UPDATE, в противном случае — INSERT. Старый алгоритм приводит к выполнению ещё одного запроса в случае UPDATE. В редких случаях база данных не сообщает об обновлении строки, даже если в ней есть строка со значением первичного ключа объекта. Например, это происходит с триггером PostgreSQL ON UPDATE, который возвращает NULL. В таких случаях можно вернуться к старому алгоритму, установив параметр select_on_save в True.

Принудительное выполнение INSERT или UPDATE

В некоторых редких случаях необходимо принудительно заставить метод save() выполнить SQL-оператор INSERT и не переходить к UPDATE. Или наоборот: выполнить обновление, если это возможно, но не вставлять новую строку. В таких случаях можно передать параметры force_insert=True или force_update=True методу save(). Передавать оба параметра нельзя: невозможно одновременно выполнить вставку и обновление!

При использовании многотабличного наследования также можно передать кортеж родительских классов в force_insert, чтобы принудительно выполнить операторы INSERT для каждого базового класса. Например:

Restaurant(pk=1, name="Bob's Cafe").save(force_insert=(Place,))

Restaurant(pk=1, name="Bob's Cafe", rating=4).save(force_insert=(Place, Rating))

Можно передать force_insert=(models.Model,), чтобы принудительно выполнить оператор INSERT для всех родительских классов. По умолчанию force_insert=True принудительно вставляет новую строку только для текущей модели.

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

Если принудительное обновление не затрагивает ни одной строки, возникает исключение NotUpdated. В предыдущих версиях возникало общее исключение django.db.DatabaseError.

Скорее всего, вам крайне редко придётся использовать эти параметры. Django почти всегда выбирает правильное действие, а попытки переопределить его могут привести к ошибкам, которые трудно обнаружить. Эта функция предназначена только для опытных пользователей.

Использование update_fields принудительно выполняет обновление аналогично force_update.

Обновление атрибутов на основе существующих полей

Иногда требуется выполнить простую арифметическую операцию над полем, например увеличить или уменьшить его текущее значение. Один из способов — выполнить вычисление в Python:

>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold += 1
>>> product.save()

Если старое значение number_sold, полученное из базы данных, было равно 10, то в базу данных будет записано значение 11.

Процесс можно сделать надёжнее, избежав состояния гонки, а также немного ускорить, задавая обновление относительно исходного значения поля, а не явно присваивая новое значение. Для такого относительного обновления Django предоставляет F expressions. С использованием F expressions предыдущий пример выглядит так:

>>> from django.db.models import F
>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold = F("number_sold") + 1
>>> product.save()

Подробнее см. документацию по F expressions и их использованию в запросах на обновление.

Указание полей для сохранения

Если методу save() передать список имён полей в именованном аргументе update_fields, будут обновлены только поля из этого списка. Это может быть полезно, если нужно обновить у объекта одно или несколько полей. Кроме того, можно немного повысить производительность, не обновляя в базе данных все поля модели. Например:

product.name = "Name changed again"
product.save(update_fields=["name"])

Аргумент update_fields может быть любым итерируемым объектом, содержащим строки. Пустой итерируемый объект update_fields отменит сохранение. Значение None приведёт к обновлению всех полей.

Указание update_fields принудительно выполнит обновление.

При сохранении модели, полученной с отложенной загрузкой (only() или defer()), обновляются только поля, загруженные из базы данных. Фактически в этом случае автоматически применяется update_fields. Если вы присвоите значение отложенному полю или измените его, оно будет добавлено в список обновляемых полей.

Field.pre_save() и update_fields

Если передан аргумент update_fields, вызываются только методы pre_save() полей update_fields. Например, это означает, что поля даты и времени с auto_now=True не будут обновлены, если они не включены в update_fields.

Удаление объектов

Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False) [источник]
Model.adelete(using=DEFAULT_DB_ALIAS, keep_parents=False)

Асинхронная версия: adelete()

Выполняет SQL-оператор DELETE для объекта. Удаляется только объект в базе данных; экземпляр Python продолжает существовать, и его поля по-прежнему содержат данные, кроме первичного ключа, которому присваивается None. Метод возвращает количество удалённых объектов и словарь с количеством удалений для каждого типа объектов.

Подробнее, в том числе об удалении объектов массово, см. в разделе Удаление объектов.

Если вы хотите изменить поведение удаления, переопределите метод delete(). Подробнее см. в разделе Переопределение предопределённых методов модели.

Иногда при использовании многотабличного наследования может потребоваться удалить только данные дочерней модели. Указание keep_parents=True сохранит данные родительской модели.

Сериализация объектов с помощью pickle

При сериализации модели с помощью pickle её текущее состояние сериализуется. После десериализации в объекте будет состояние экземпляра модели на момент сериализации, а не данные, которые в данный момент находятся в базе данных.

Файлы pickle несовместимы между версиями

Сериализованные модели действительны только для той версии Django, с помощью которой они были созданы. Если вы создадите файл pickle с Django версии N, нет гарантии, что его можно будет прочитать в Django версии N+1. Не следует использовать pickle для долгосрочного архивного хранения.

Поскольку ошибки совместимости pickle, например незаметное повреждение объектов, бывает трудно диагностировать, при попытке десериализовать модель в версии Django, отличной от той, в которой она была сериализована, возникает исключение RuntimeWarning.

Другие методы экземпляров модели

Некоторые методы объектов имеют особое назначение.

__str__()

Model.__str__() [источник]

Метод __str__() вызывается каждый раз, когда вы вызываете str() для объекта. Django использует str(obj) в нескольких местах. В частности, для отображения объекта в административном интерфейсе Django и для подстановки значения в шаблон при отображении объекта. Поэтому метод __str__() всегда должен возвращать удобное для чтения человеком представление модели.

Например:

from django.db import models


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

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

__eq__()

Model.__eq__() [источник]

Метод сравнения определён так, что экземпляры с одинаковым значением первичного ключа и одинаковым конкретным классом считаются равными. Исключение составляют экземпляры со значением первичного ключа None: они не равны ничему, кроме самих себя. Для прокси-моделей конкретный класс определяется как первый родительский класс модели, не являющийся прокси; для всех остальных моделей это просто класс модели.

Например:

from django.db import models


class MyModel(models.Model):
    id = models.AutoField(primary_key=True)


class MyProxyModel(MyModel):
    class Meta:
        proxy = True


class MultitableInherited(MyModel):
    pass


# Primary keys compared
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) != MyModel(id=2)
# Primary keys are None
MyModel(id=None) != MyModel(id=None)
# Same instance
instance = MyModel(id=None)
instance == instance
# Proxy model
MyModel(id=1) == MyProxyModel(id=1)
# Multi-table inheritance
MyModel(id=1) != MultitableInherited(id=1)

__hash__()

Model.__hash__() [источник]

Метод __hash__() основан на значении первичного ключа экземпляра. Фактически он равен hash(obj.pk). Если у экземпляра нет значения первичного ключа, возникает исключение TypeError (иначе метод __hash__() возвращал бы разные значения до и после сохранения экземпляра, но изменение значения __hash__() экземпляра запрещено в Python).

get_absolute_url()

Model.get_absolute_url()

Определите метод get_absolute_url(), чтобы указать Django, как вычислять канонический URL объекта. Для вызывающего кода этот метод должен выглядеть так, будто он возвращает строку, которую можно использовать для доступа к объекту по HTTP.

Например:

def get_absolute_url(self):
    return "/people/%i/" % self.id

Хотя этот код корректен и прост, он может быть не самым переносимым способом написания такого метода. Обычно лучше использовать функцию reverse().

Например:

def get_absolute_url(self):
    from django.urls import reverse

    return reverse("people-detail", kwargs={"pk": self.pk})

Django использует get_absolute_url(), например, в приложении административного интерфейса. Если объект определяет этот метод, на странице редактирования объекта появится ссылка «Посмотреть на сайте», ведущая непосредственно к общедоступному представлению объекта, указанному в get_absolute_url().

Некоторые другие компоненты Django, например инфраструктура лент syndication, также используют get_absolute_url(), если он определён. Если для каждого экземпляра модели имеет смысл иметь уникальный URL, следует определить get_absolute_url().

Предупреждение

Не следует формировать URL на основе непроверенных пользовательских данных, чтобы снизить риск подмены ссылок или перенаправлений:

def get_absolute_url(self):
    return "/%s/" % self.name

Если self.name равен '/example.com', этот код возвращает '//example.com/', что является допустимым URL относительно схемы, но не ожидаемым '/%2Fexample.com/'.

В шаблонах рекомендуется использовать get_absolute_url() вместо жёстко заданных URL объектов. Например, такой код шаблона неудачен:

<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>

Этот код шаблона гораздо лучше:

<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>

Логика здесь такова: если вы измените структуру URL объектов, даже для такой мелочи, как исправление опечатки, вам не придётся искать все места, где может создаваться URL. Укажите его один раз в get_absolute_url() и вызывайте это место из всего остального кода.

Примечание

Строка, возвращаемая get_absolute_url(), должна содержать только символы ASCII (этого требует спецификация URI, RFC 3986, раздел 2) и при необходимости должна быть закодирована в формате URL.

Код и шаблоны, вызывающие get_absolute_url(), должны иметь возможность использовать результат напрямую, без дополнительной обработки. Если вы используете строки с символами за пределами ASCII, для этого можно воспользоваться функцией django.utils.encoding.iri_to_uri().

Дополнительные методы экземпляров

Помимо save() и delete(), объект модели может иметь некоторые из следующих методов:

Model.get_FOO_display()

Для каждого поля, у которого задан параметр choices, объект будет иметь метод get_FOO_display(), где FOO — имя поля. Этот метод возвращает «понятное для человека» значение поля.

Например:

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=2, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
Model.get_next_by_FOO(**kwargs)
Model.get_previous_by_FOO(**kwargs)

Для каждого поля DateField и DateTimeField, у которого не задан параметр null=True, объект будет иметь методы get_next_by_FOO() и get_previous_by_FOO(), где FOO — имя поля. Эти методы возвращают следующий и предыдущий объект относительно значения поля даты и при необходимости возбуждают исключение DoesNotExist.

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

Обратите внимание: если значения даты совпадают, эти методы используют первичный ключ для разрешения равенства. Это гарантирует, что записи не будут пропущены или продублированы. Это также означает, что эти методы нельзя использовать для несохранённых объектов.

Переопределение дополнительных методов экземпляра

В большинстве случаев переопределение или наследование get_FOO_display(), get_next_by_FOO() и get_previous_by_FOO() должно работать ожидаемым образом. Однако, поскольку эти методы добавляются метаклассом, учесть все возможные структуры наследования непрактично. В более сложных случаях следует переопределить Field.contribute_to_class(), чтобы задать необходимые методы.

Другие атрибуты

_state

Model._state

Атрибут _state ссылается на объект ModelState, отслеживающий жизненный цикл экземпляра модели.

Объект ModelState имеет два атрибута: adding — флаг, равный True, если модель ещё не сохранена в базе данных, — и db — строку с псевдонимом базы данных, из которой был загружен экземпляр или в которую он был сохранён.

У вновь созданных экземпляров значения adding=True и db=None, поскольку они ещё не были сохранены. У экземпляров, полученных из QuerySet, значения adding=False и db будут заданы псевдонимом связанной базы данных.

_is_pk_set()

Model._is_pk_set() [источник]
Добавлено в Django 5.2.

Метод _is_pk_set() возвращает значение, указывающее, задан ли pk экземпляра модели. Он абстрагирует определение первичного ключа модели, обеспечивая единообразное поведение независимо от конкретной конфигурации pk.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/models/instances/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API