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

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

В примерах будут использованы :ref:` примеры моделей web-блога <queryset-model-example>` представленные в разделе о выполнении запросов.

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

Чтобы создать объект модели, просто создайте ее экземпляр как любого другого класса 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")
    

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

Проверка объектов модели проходив в три этапа:

  1. Проверка полей модели - Model.clean_fields()

  2. Проверка всего объекта - Model.clean()

  3. Проверка уникальности полей - Model.validate_unique()

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

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

Model.full_clean(exclude=None, validate_unique=True)
Changed in Django 1.6:

The validate_unique parameter was added to allow skipping Model.validate_unique(). Previously, Model.validate_unique() was always called by full_clean.

Этот метод вызывает Model.clean_fields(), Model.clean(), и Model.validate_unique()`(если ``validate_unique`() равно 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 programatically.
    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

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

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

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

Также full_clean() выполняет все проверки на уникальность модели.

Model.validate_unique(exclude=None)

Этот метод похож на clean_fields(), но проверяет уникальность полей, используя все определенные правила, а не значения полей.Необязательный аргумент exclude используется, чтобы исключить часть полей из проверки. Вызывает исключение ValidationError, если поле не прошло проверку.

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

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

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

Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None])

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

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

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

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

>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id     # Returns None, because b 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 нового объекта при сохранении, просто укажите его:

>>> 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`_ ниже.

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

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

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

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

  2. Предварительная обработка данных. Каждое поле объекта выполняет изменения значения поля при необходимости.

    Большинство полей не выполняют предварительную обработку данных — значение полей сохраняется как оно есть. Она выполняется для полей с особым поведением. Например, если ваша модель содержит поле DateField с auto_now=True, на этапе предварительной обработки значение поля будет установлено в текущую дату. (Наша документация пока не содержит список полей с “особым поведением”.)

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

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

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

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

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

Как Django определят использовать UPDATE или INSERT

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

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

  • Если первичный ключ не указан, или UPDATE ничего не обновил, Django выполнит INSERT.

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

Changed in Django 1.6:

Previously Django did a SELECT when the primary key attribute was set. If the SELECT found a row, then Django did an UPDATE, otherwise it did an INSERT. The old algorithm results in one more query in the UPDATE case. There are some rare cases where the database doesn’t report that a row was updated even if the database contains a row for the object’s primary key value. An example is the PostgreSQL ON UPDATE trigger which returns NULL. In such cases it is possible to revert to the old algorithm by setting the select_on_save option to True.

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

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

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

Использование update_fields инициирует обновление объекта, как и при вызове force_update.

Обновление значений полей

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

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

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

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

>>> product = Products.objects.get(pk=product.pk)
>>> print(product.number_sold)
42

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

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

New in Django 1.5.

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

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

update_fields может принимать любой итератор строк. Пустой update_fields пропустит сохранение. None сохранит все поля.

Указав update_fields вы инициируете редактирование записи.

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

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

Model.delete([using=DEFAULT_DB_ALIAS])

Выполняет SQL DELETE запрос для объекта. Удаляет объекты только из базы данных; объекты Python будут существовать и содержать данные.

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

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

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

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

Примечание

В Python 3, так как все строки являются Unicode строками, используйте только метод __str__() (метод __unicode__() устарел). Если вам необходима совместимость с Python 2, Можете декорировать ваш класс модели декоратором python_2_unicode_compatible().

__unicode__

Model.__unicode__()

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

Например:

from django.db import models

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

    def __unicode__(self):
        return u'%s %s' % (self.first_name, self.last_name)

Если вы определили метод __unicode__() и не определили __str__(), Django самостоятельно добавит метод __str__() который вызывает __unicode__(), затем преобразует результат в строку в кодировке UTF-8. Это рекомендуемый подход: определить только __unicode__() и позволить Django самостоятельно преобразовать в строку при необходимости.

__str__

Model.__str__()

Метод __str__() вызывается, когда вы применяет функцию str() к объекту. Django использует его если нужно вывести результат функции repr() (например, при отладке). Поэтому, вы должны всегда возвращать в методе __str__() красивое и удобное для восприятия представление объекта. Определять метод __str__() не обязательно, если вы определили метод __unicode__().

Предыдущий пример метода __unicode__() может аналогично использоваться и в __str__():

from django.db import models
from django.utils.encoding import force_bytes

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

    def __str__(self):
        # Note use of django.utils.encoding.force_bytes() here because
        # first_name and last_name will be unicode strings.
        return force_bytes('%s %s' % (self.first_name, self.last_name))

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.core.urlresolvers import reverse
    return reverse('people.views.details', args=[str(self.id)])

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

Кроме того, несколько приложений Django также используют этот метод, например syndication feed framework. Если объект модели представляет какой-то уникальный URL, вам стоит определить метод get_absolute_url().

Хорошая практика использовать 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 2396) и быть закодированной для 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 = (
        (u'S', u'Small'),
        (u'M', u'Medium'),
        (u'L', u'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
u'L'
>>> p.get_shirt_size_display()
u'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, если объект не существует.

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

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