Этот раздел описывает Model API. Изложенный материал опирается на материал, изложенный в разделах о моделях и выполнении запросов, возможно вам следует прочитать их перед прочтением этого раздела.
В примерах будут использованы :ref:` примеры моделей web-блога <queryset-model-example>` представленные в разделе о выполнении запросов.
Что бы создать объект модели, просто создайте ее экземпляр как любого другого класса Python:
Именованные аргументы это названия полей определенных в модели. Создание экземпляра модели не выполняет никаких запросов к базе данных; для сохранение вызовите метод save().
Проверка объектов модели проходив в три этапа:
Проверка полей модели
Проверка всего объекта
Проверка уникальности полей
Все три этапа выполняются при вызове метода full_clean().
При использовании ModelForm, вызов is_valid() выполняет проверку для всех полей включенных в форму. Подробности смотрите раздел о ModelForm. Вы должны использовать метод full_clean() модели только если собираетесь самостоятельно обрабатывать ошибки валидности, или если ModelForm не содержит поля, которые должны проверяться.
Этот метод вызывает Model.clean_fields(), Model.clean(), и Model.validate_unique() в указанном порядке и вызывает исключение ValidationError, которое содержит атрибут message_dict с ошибками всех трех этапов проверки.
Необязательный аргумент exclude используется, что бы исключить часть полей из проверки. ModelForm использует этот аргумент для полей не включенных в форму, так как ошибки для этих полей не могут быть исправлены пользователем.
Заметим, full_clean() не вызывается ни при вызове метода save() модели, ни в результате проверки в ModelForm. Этот метод используется для выполнения полной проверки объекта модели, созданного в коде.
Например:
try:
article.full_clean()
except ValidationError, e:
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programatically.
Первым делом full_clean() выполняет проверку каждого поля.
Этот метод проверяет все поля модели. Необязательный аргумент exclude используется, что бы исключить часть полей из проверки. Если одно из полей не пройдет проверку, будет вызвано исключение ValidationError.
Следующим этапов проверки в full_clean() будет вызов метода Model.clean(). Этот метод должен быть переопределен, если вам нужна дополнительная проверка модели.
Этот метод должен быть переопределен, если вам нужна дополнительная проверка модели или изменить значения атрибутов. Для объектов, вы можете определить его для автоматического определения полей, или для проверки, которая требует значения нескольких полей:
def clean(self):
from django.core.exceptions import ValidationError
# 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.datetime.now()
Любое исключение ValidationError вызванное в Model.clean() будет сохранено со специальным ключом в словаре ошибок, NON_FIELD_ERRORS, который используется для ошибок относящихся ко всей модели, а не конкретному полю:
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
article.full_clean()
except ValidationError, e:
non_field_errors = e.message_dict[NON_FIELD_ERRORS]
Так же full_clean() выполняет все проверки на уникальность модели.
Этот метод похож на clean_fields(), но проверяет уникальность полей, используя все определенные правила, а не значения полей.Необязательный аргумент exclude используется, что бы исключить часть полей из проверки. Вызывает исключение ValidationError, если поле не прошло проверку.
Заметим, при использовании аргумента exclude, все правила определенные в unique_together, включающие одно из указанных полей, не будет учитываться при проверке.
Что бы сохранить объект в базе данных, используйте save():
Если вы хотите изменить процесс сохранения, переопределите метод 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.
Независимо от того, определили вы первичный ключ самостоятельно, или позволили 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 выполняет следующие шаги:
Посылается сигнал pre-save. Посылается сигнал django.db.models.signals.pre_save, позволяя функциям, обрабатывающим этот сигнал, выполнить какие-либо действия.
Предварительная обработка данных. Каждое поле объекта выполняет изменения значения поля при необходимости.
Большинство полей не выполняют предварительную обработку данных — значение полей сохраняется как оно есть. Она выполняется для полей с особым поведением. Например, если ваша модель содержит поле DateField с auto_now=True, на этапе предварительной обработки значение поля будет установлено в текущую дату. (Наша документация пока не содержит список полей с “особым поведением”.)
Подготовка данных для базы данных. Каждое поле преобразует текущее значение к типу данных, которой может быть сохранен в базу данных.
Большинство полей не требует подготовки данных. Простые типы данных, такие как числа и строки, уже ‘готовы к сохранению’ как объекты Python. Однако, большинство сложных типов данных требуют некоторой модификации.
Например, поле DateField использует объект Python datetime для хранения значения. База данных не принимает объект datetime, поэтому значение поля должно быть преобразовано в строковое представление даты в соответствии стандарту ISO перед сохранением в базу данных.
Сохранение данных в базе данных. Предварительно обработанные, подготовленные данные формируются в SQL запрос, который выполняется в базе данных.
Посылается сигнал post-save. Посылается сигнал django.db.models.signals.post_save, позволяя функциям, обрабатывающим этот сигнал, выполнить какие-либо действия.
Вы уже заметили что объекты модели используют метод save() как для создания так и для изменения записи в базе данных. Django самостоятельно определяет использовать INSERT или UPDATE. При вызове save(), Django следует такому алгоритму:
Если атрибут первичного ключа объекта содержи значение равное True (например, не None или не пустая строка), Django выполняет SELECT запрос, что бы определить существует ли запись с указанным первичным ключом.
Если запись с указанным первичным ключом уже существует, Django выполняет UPDATE запрос.
Если первичный ключ не указан, или указан, но запись не существует в базе данных, Django выполнит INSERT.
Будьте осторожны, явно указывая значение первичного ключа при сохранении нового объекта, если вы не уверенны, что этот первичный ключ не используется. Более подробно об этом читайте `Explicitly specifying auto-primary-key values`_ и Принудительное выполнение INSERT или UPDATE.
В редких случаях, может понадобиться принудительно заставить метод save() выполнить INSERT запрос вместо UPDATE. Или наоборот: обновить, при возможности, но не добавлять новую запись. В этом случае вы можете указать аргумент force_insert=True или force_update=True для метода save(). Очевидно, не правильно использовать оба аргумента вместе: вы не можете добавлять и обновлять одновременно!
Вряд ли вам понадобится использовать эти параметры. Django почти всегда сделает то, что вам нужно, и переопределение такого поведения может привести к ошибкам, которые трудно отследить. Эта функция предназначена для опытных пользователей.
Иногда вам может понадобиться выполнить простые арифметические операции над полями, такие как увеличить или уменьшить текущее значение. Очевидный способ сделать это:
>>> 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() и его использование в запросах обновления.
Выполняет DELETE запрос для объекта. Удаляет объекты только из базы данных; объекты Python будут существовать и содержать данные.
Подробности, включая как удалить множество объектов, смотрите в Удаление объектов.
Если вам нужно изменить процесс удаления, переопределите метод delete(). Подробности в Переопределение методов модели.
Несколько методов имеют специальное назначение.
Метод __unicode__() вызывается когда вы применяете функцию unicode() к объекту. Django использует unicode(obj) (или похожую функцию str(obj)) вы нескольких местах. В частности, для отображения объектов в интерфейсе администратора Django и в качестве значения, вставляемого в шаблон, при отображении объекта. Поэтому, вы должны всегда возвращать в методе __unicode__() красивое и удобное для восприятия представление объекта.
Например:
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__() вызывается, когда вы применяет функцию str() к объекту. Django использует его если нужно вывести результат функции repr() (например, при отладке). Поэтому, вы должны всегда возвращать в методе __str__() красивое и удобное для восприятия представление объекта. Определять метод __str__() не обязательно, если вы определили метод __unicode__().
Предыдущий пример метода __unicode__() может аналогично использоваться и в __str__():
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.smart_str() here because
# first_name and last_name will be unicode strings.
return smart_str('%s %s' % (self.first_name, self.last_name))
Определите метод get_absolute_url() что бы указать Django как вычислить URL для объекта. Метод должен вернуть строку, которая может быть использована в HTTP запросе.
Например:
def get_absolute_url(self):
return "/people/%i/" % self.id
(Хотя это код правильный и простой, но такой подход не самый лучший для создания подобных методов. декоратор permalink(), описанный ниже, предоставляет лучший подход и вам следует прочитать о нем перед тем, как приняться за написание кода.)
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 символы.
То, как мы написали метод get_absolute_url(), немного нарушает принцип DRY: URL для объекта определяется и в URLconf, и в модели.
Вы можете использовать определение URL из URLconf в вашей модели, используя декоратор permalink:
Этот декоратор принимает названия шаблона URL (либо имя представления, либо название шаблона URL) и список позиционных или именованных аргументов шаблона URLconf для вычисления правильного и полного URL. Он возвращает правильный URL, с учетом всех аргументов.
Декоратор permalink это эквивалент шаблонного тега url и обертка для функции django.core.urlresolvers.reverse().
Пример покажет как использовать permalink(). Предположим у на есть такой URLconf:
(r'^people/(\d+)/$', 'people.views.details'),
...ваша модель может определить метод get_absolute_url() таким образом:
from django.db import models
@models.permalink
def get_absolute_url(self):
return ('people.views.details', [str(self.id)])
Добавим еще одну строку в URLconf:
(r'/archive/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', archive_view)
...теперь permalink() можно использовать таким образом:
@models.permalink
def get_absolute_url(self):
return ('archive_view', (), {
'year': self.created.year,
'month': self.created.strftime('%m'),
'day': self.created.strftime('%d')})
Обратите внимание, мы указали пустую последовательность вторым параметром, потому что хотим использовать только именованные аргументы.
Таким образом, мы связали абсолютный путь модели с представлением, которое отображает ее, без повторного упоминания информации про URL. Вы можете использовать метод get_absolute_url() в шаблоне, как было показано выше.
В некоторых случаях, таких, как использование общих представлений или повторное использование представления для нескольких моделей, использование представления может обескуражить определение URL (так как несколько шаблонов используют одно представление). Для таких случаев, Django имеет именованные шаблоны URL. При их использовании, можно добавить название для шаблона URL, и использовать его вместо функции представления при определении URL, именованные шаблоны URL определяются заменой кортежа в URLconf на вызов функции url):
from django.conf.urls import patterns, url, include
url(r'^people/(\d+)/$', 'blog_views.generic_detail', name='people_view'),
...а затем использовать это название вместо названия функции представления для вычисления URL:
from django.db import models
@models.permalink
def get_absolute_url(self):
return ('people_view', [str(self.id)])
Подробности вы можете узнать в разделе о менеджере URL.
В дополнение к методам save(), delete(), объект модели может содержать некоторые из этих методов:
Для каждого поля, которое содержит choices, объект будет иметь метод get_FOO_display(), где FOO имя поля. Этот метод возвращает удобное для восприятия название для значения поля. Например, в следующей модели:
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
class Person(models.Model):
name = models.CharField(max_length=20)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
...каждый объект Person будет иметь метод get_gender_display(). Например:
>>> p = Person(name='John', gender='M')
>>> p.save()
>>> p.gender
'M'
>>> p.get_gender_display()
'Male'
Для каждого поля DateField и DateTimeField, которое не содержит null=True, объект будет иметь методы get_next_by_FOO() и get_previous_by_FOO(), где FOO название поля. Они возвращают следующий и предыдущий объект в соответствии со значением этого поля, вызывая соответствующее исключение DoesNotExist, если объект не существует.
Оба метода принимают не обязательные именованные аргументы, которые должны быть в формате описанном в разделе об операциях фильтрации.
Заметим, что в случае одинаковых значений даты, эти методы будут использовать значение первичного ключа, для определения порядка объектов. Это гарантирует, что записи не будут пропущены или дублированы. Это так же означает, что вы не можете использовать эти методы для не сохраненных объектов.
Mar 30, 2016