• 3.1
  • 3.2
  • 6.1
  • Версия документации: 5.0

Объект модели

Этот раздел описывает Model API. Изложенный материал опирается на материал, изложенный в разделах о моделях и выполнении запросов, возможно вам следует прочитать их перед прочтением этого раздела.

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

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

Чтобы создать объект модели, просто создайте ее экземпляр как любого другого класса 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, поэтому можно использовать cls(**(zip(field_names, values))) для создания объекта. Если все поля модели присутствуют, порядок значений в values будет таким, каким их ожидает __init__(). И вы можете создать объект с помощью cls(*values). Все поля модели загружены, если cls._deferred равен False.

Помимо создания новой модели, метод 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(), чтобы прояснить, как это делается. В этом случае можно было бы использовать вызов 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, from_queryset=None)
Model.arefresh_from_db(using=None, fields=None, from_queryset=None)

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

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

  1. Все загруженные поля модели будут обновлены значениями из базы данных.

  2. Любые кэшированные отношения удаляются из перезагруженного экземпляра.

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

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

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

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())
Changed in Django 5.1:

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

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

UniqueConstraints, определенные в Meta.constraints, проверяются Model.validate_constraints().

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

Наконец, full_clean() проверит любые другие ограничения вашей модели.

Model.validate_constraints(exclude=None)

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

Процесс сохранения модели имеет ряд особенностей описанных ниже.

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

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

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

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

О том, как это определяется, смотрите How Django knows to UPDATE vs. INSERT (FIXME) ниже.

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

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

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

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

  1. Посылается сигнал post-save. Посылается сигнал django.db.models.signals.post_save, позволяя функциям, обрабатывающим этот сигнал, выполнить какие-либо действия.

  2. Предварительная обработка данных. Метод pre_save() каждого поля вызывается для выполнения любого необходимого автоматического изменения данных. Например, поля даты/времени переопределяют pre_save() для реализации auto_now_add и auto_now.

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

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

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

  4. Сохранение данных в базе данных. Предварительно обработанные, подготовленные данные формируются в SQL запрос, который выполняется в базе данных.

  5. Посылается сигнал post-save. Посылается сигнал django.db.models.signals.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.

Будьте осторожны, явно указывая значение первичного ключа при сохранении нового объекта, если вы не уверенны, что этот первичный ключ не используется. Более подробно об этом читайте Explicitly specifying auto-primary-key values (FIXME) и Принудительное выполнение INSERT или UPDATE.

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

Changed in Django 5.0:

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

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

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

При использовании многотабличного наследования <multi-table-inheritance>` также можно предоставить кортеж родительских классов для 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.

Changed in 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 выражения для выполнения такого рода относительного обновления. Используя выражения F, предыдущий пример выражается так:

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

Подробности смотрите в описании объекта F и его использование в запросах обновления.

Указываем какие поля сохранять

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

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

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

Указав 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 по-прежнему будет существовать, и в его полях будут по-прежнему данные, за исключением первичного ключа, установленного на «Нет». Этот метод возвращает количество удаленных объектов и словарь с количеством удалений для каждого типа объекта.

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

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

В некоторых случаях при multi-table наследовании вам может понадобиться удалить только данные дочерней модели. Передав аргумент keep_parents=True, вы сохраните данные родительской модели.

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

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

нельзя переносить «pickles» между версиями

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

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

Остальные методы модели

Несколько методов имеют специальное назначение.

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

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

Например:

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__ разные значения перед и после сохранения объекта, что запрещено в 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 feed framework. Если объект модели представляет какой-то уникальный 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(), если используете unicode-строку, которая содержит не 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, установленные в псевдоним связанной базы данных.

Back to Top