Spec-Zone.ru › Django 4.2

Ссылка на экземпляр модели

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

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

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

Для создания нового экземпляра модели инициализируйте её как любой другой класс Python:

class Model(**kwargs)

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

Примечание

Возможно, вы захотите настроить модель, переопределив метод __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() для …”, чтобы подтвердить правильность проверки.

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

В более ранних версиях ограничения не проверялись во время проверки модели.

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() — очистка каждого отдельного поля.

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

был добавлен аргумент validate_constraints.

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

значение exclude теперь преобразуется в set, а не в list.

Model.clean_fields(exclude=None)

Этот метод проверит все поля вашей модели. Необязательный аргумент exclude позволяет указать множество имён полей, которые нужно исключить из проверки. Он сгенерирует 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() проверит все остальные ограничения вашей модели.

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

В более ранних версиях, UniqueConstraint проверялись с помощью validate_unique().

Model.validate_constraints(exclude=None)
Новое в Django 4.1.

Этот метод проверяет все ограничения, определенные в 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.

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

Учитывая вышеприведенный пример блога, этот пример перезапишет предыдущую запись в базе данных:

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

См. Как Django определяет обновление или вставку ниже, чтобы понять причину этого.

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

Если вы используете 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 абстрагирует необходимость использования INSERT или UPDATE SQL-запросов. В частности, когда вы вызываете save(), и атрибут первичного ключа объекта не определяет default, Django следует этому алгоритму:

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

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

Важный момент: будьте осторожны, не указывайте явно значение первичного ключа при сохранении новых объектов, если вы не можете гарантировать, что значение первичного ключа не используется. Более подробная информация об этом нюансе приведена в разделе Явное указание значений первичного ключа auto выше и Принудительное выполнение 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(). Передача обоих параметров является ошибкой: нельзя одновременно вставлять и обновлять!

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

Изменено в 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#раздел-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/4.2/ref/models/instances/

Spec-Zone.ru

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