Spec-Zone.ru › Django 5.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, *args, **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(*args, **kwargs)

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

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

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

>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field  # Loads the field from the database
Model.refresh_from_db(using=None, fields=None)
Model.arefresh_from_db(using=None, fields=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)
Model.get_deferred_fields()

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

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

arefresh_from_db() метод был добавлен.

Валидация объектов

Валидация модели включает четыре шага:

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

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

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

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

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

Вы всегда должны проверять отсутствие сообщений в логах в логере django.db.models, таких как “Получена ошибка базы данных при вызове check() для …”, чтобы убедиться, что валидация выполнена корректно.

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

UniqueConstraints, определённые в 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() метод. Более подробная информация об этом есть в разделе Переопределение предопределённых методов модели.

Процесс сохранения модели также имеет некоторые нюансы; см. разделы ниже.

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

asave() метод был добавлен.

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

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

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

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

Как Django определяет UPDATE или INSERT

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

  • Если атрибут первичного ключа объекта установлен на значение, которое оценивается как True (то есть значение, отличное от 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.

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

Добавлен параметр Field.db_default.

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

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

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

Добавлена поддержка передачи кортежа родительских классов в force_insert.

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

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

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

adelete() метод был добавлен.

Сериализация объектов

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

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

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

Поскольку ошибки совместимости сериализованных копий могут быть трудно диагностированы, например, с молчаливым повреждением объектов, при попытке десериализовать модель в версии 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, таких как рамка подписки на ленты новостей, используют 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#section-2) и быть закодированной в URL, если это необходимо.

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

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

В дополнение к 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 установленные в псевдоним связанной базы данных.

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

Spec-Zone.ru

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