Этот раздел описывает Model API. Изложенный материал опирается на материал, изложенный в разделах о моделях и выполнении запросов, возможно вам следует прочитать их перед прочтением этого раздела.
В примерах будут использованы :ref:` примеры моделей web-блога <queryset-model-example>` представленные в разделе о выполнении запросов.
Чтобы создать объект модели, просто создайте ее экземпляр как любого другого класса Python:
Именованные аргументы – это названия полей определенных в модели. Создание экземпляра модели не выполняет никаких запросов к базе данных; для сохранения вызовите метод save().
Примечание
Возможно, вам захочется переопределить метод __init__. В таком случае не переопределяйте сигнатуру вызова этого метода, иначе объект модели может не сохраняться. Вместо переопределения __init__ лучше используйте один из следующих подходов:
Добавить метод класса в модель:
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")
Добавить метод в менеджер модели(лучший вариант):
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")
Метод 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 нового объекта модели.
В этом примере вы можете увидеть как сохранить начальные загруженные значений полей и проверять их при сохранении:
@classmethod
def from_db(cls, db, field_names, values):
# default implementation of from_db() (could be replaced
# with super())
if cls._deferred:
instance = cls(**zip(field_names, values))
else:
instance = cls(*values)
instance._state.adding = False
instance._state.db = db
# customization to store the original field values on the instance
instance._loaded_values = zip(field_names, values)
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().
Если вам необходимо обновить значения модели из базы данных, вы можете использовать refresh_from_db(). Если вызвать этот метод без аргументов, произойдет следующее:
Все загруженные поля модели будут обновлены значениями из базы данных.
Если ранее загруженные внешние связи уже не верны, они будут удалены. Например, если объект содержит внешнюю связь на модель Author, и obj.author_id != obj.author.id, obj.author будет очищен и при следующем обращении будет загружен объект со значением obj.author_id.
Обратите внимание, загружаются только поля модели. Аннотации и прочие специфические значения не будут загружены.
Перезагрузка выполняется из базы данных, из которой объект был изначально загружен, или из базы данных по умолчанию, если база данных не была явно указана. Аргумент 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(ExampleModel, self).refresh_from_db(using, fields, **kwargs)
Возвращает список текущих отложенных полей для экземпляра модели.
Проверка объектов модели проходив в три этапа:
Проверка полей модели - Model.clean_fields()
Проверка всего объекта - Model.clean()
Проверка уникальности полей - Model.validate_unique()
Все три этапа выполняются при вызове метода full_clean().
При использовании ModelForm, вызов is_valid() выполняет проверку для всех полей, включенных в форму. Подробности смотрите раздел о ModelForm. Вы должны использовать метод full_clean() модели только если собираетесь самостоятельно обрабатывать ошибки валидности, или если ModelForm не содержит поля, которые должны проверяться.
Этот метод вызывает 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 programmatically.
pass
Первым делом full_clean() выполняет проверку каждого поля.
Этот метод проверяет все поля модели. Необязательный аргумент exclude используется, чтобы исключить часть полей из проверки. Если одно из полей не пройдет проверку, будет вызвано исключение ValidationError.
Следующим этапов проверки в full_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()
Обратите внимание, Model.full_clean(), как и метод модели clean(), не вызываются при вызове save().
Любое исключение 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]
Чтобы добавить ошибку валидации к определенному полю, создайте экземпляр 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.'})
...
Также 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 (FIXME) ниже.
Явное определение первичного ключа в основном полезно при сохранении множества объектов, когда вы уверенны что все значения уникальны.
При сохранении объекта 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 выполняет UPDATE запрос.
Если первичный ключ не указан, или 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.
В редких случаях, может понадобиться принудительно заставить метод 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()
Подробности смотрите в описании объекта F и его использование в запросах обновления.
Если в save() передать именованный аргумент update_fields со списком полей модели, только эти поля будут обновлены. Это может пригодиться, если вы хотите обновить одно или несколько полей. Таким образом можно получить небольшой прирост в производительности. Например:
product.name = 'Name changed again'
product.save(update_fields=['name'])
update_fields может принимать любой итератор строк. Пустой update_fields пропустит сохранение. None сохранит все поля.
Указав update_fields вы инициируете редактирование записи.
Если модель была загружена не со всеми полями (через only() или defer()), только загруженные поля будут сохранены. В этом случае update_fields будет определен автоматически. Если значение поля будет изменено, оно будет добавлено в список для обновления.
Выполняет SQL DELETE запрос для объекта. Удаляет объекты только из базы данных; объекты Python будут существовать и содержать данные.
Подробности, включая как удалить множество объектов, смотрите в Удаление объектов.
Если вам нужно изменить процесс удаления, переопределите метод delete(). Подробности в Переопределение методов модели.
При упаковке объекта модели с помощью pickle сохраняется текущее состояние. При распаковке объекта он содержит объект модели в состоянии на момент упаковки, а не текущее состояние в базе данных.
Несколько методов имеют специальное назначение.
Примечание
В Python 3, так как все строки являются Unicode строками, используйте только метод __str__() (метод __unicode__() устарел). Если вам необходима совместимость с Python 2, Можете декорировать ваш класс модели декоратором python_2_unicode_compatible().
Метод __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__() вызывается когда вы применяете функцию str() к объекту. В Python 3 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 '%s %s' % (self.first_name, self.last_name)
В Python 2 Django использует __str__, если нужно вывести результат функции repr() (например, при отладке). Определять метод __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))
Метод проверки равенства по умолчанию работает следующим образом: если два объекта содержат одинаковый первичный ключ и являются экземплярами одно класса, тогда они равны. Для прокси-моделей класс определяется поиском первого не прокси родительского класса. Для всех остальных моделей - это просто класс модели.
Например:
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
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) == MyProxyModel(id=1)
MyModel(id=1) != MultitableInherited(id=1)
MyModel(id=1) != MyModel(id=2)
В предыдущих версиях только экземпляры одно класса с одинаковым первичным ключом считались равными.
Метод __hash__ использует значение первичного ключа. На самом деле выполняется hash(obj.pk). Если первичный ключ не определен, будет вызвано исключение TypeError (иначе __hash__ разные значения перед и после сохранения объекта, что запрещено в Python).
В предыдущих версиях объекты без первичного ключа можно было хешировать.
Определите метод 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().
Предупреждение
При создания 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 2396) и быть закодированной для URL, если необходимо.
Код и шаблоны, использующие get_absolute_url(), должны иметь возможность использовать результат без обработки. Вы можете использовать функцию django.utils.encoding.iri_to_uri(), если используете unicode-строку, которая содержит не ASCII символы.
В дополнение к методам save(), delete(), объект модели может содержать некоторые из этих методов:
Для каждого поля, которое содержит 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'
Для каждого поля DateField и DateTimeField, которое не содержит null=True, объект будет иметь методы get_next_by_FOO() и get_previous_by_FOO(), где FOO название поля. Они возвращают следующий и предыдущий объект в соответствии со значением этого поля, вызывая соответствующее исключение DoesNotExist, если объект не существует.
Оба метода используют менеджер по умолчанию модели. Если вам необходимо использовать свой менеджер, которые должен выполнить какую-то фильтрацию, или просто добавить дополнительную фильтрацию, в методы можно передать дополнительные аргументы, которые должны соответствовать формату операторов фильтрации.
Заметим, что в случае одинаковых значений даты, эти методы будут использовать значение первичного ключа, для определения порядка объектов. Это гарантирует, что записи не будут пропущены или дублированы. Это также означает, что вы не можете использовать эти методы для не сохраненных объектов.
Jun 02, 2016