Выполнение запросов¶
После создания модели, Django автоматически создает API для работы с базой данных, который позволяет вам создавать, получать, изменять и удалять объекты. Этот раздел расскажет вам как использовать этот API. В описании моделей вы можете найти список всех существующих опций поиска.
Throughout this guide (and in the reference), we’ll refer to the following models, which comprise a Weblog application:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
Создание объектов¶
Для представления данных таблицы в виде объектов Python, Django использует интуитивно понятную систему: класс модели представляет таблицу, а экземпляр модели - запись в этой таблице.
Чтобы создать объект, создайте экземпляр класса модели, указав необходимые поля в аргументах и вызовите метод save() чтобы сохранить его в базе данных.
Assuming models live in a file mysite/blog/models.py, here’s an example:
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
В результате выполнения этого кода будет создан INSERT SQL-запрос. Django не выполняет запросов к базе данных, пока не будет вызван метод save().
Метод save() ничего не возвращает.
Сохранение изменений в объектах¶
Для сохранения изменений в объект, который уже существует в базе данных, используйте save().
Given a Blog instance b5 that has already been saved to the database,
this example changes its name and updates its record in the database:
>>> b5.name = 'New name'
>>> b5.save()
В результате выполнения этого кода будет создан UPDATE SQL запрос. Django не выполняет каких либо запросов к базе данных, пока не будет вызван метод save().
Сохранение полей ForeignKey и ManyToManyField¶
Updating a ForeignKey field works exactly the same
way as saving a normal field – assign an object of the right type to the field
in question. This example updates the blog attribute of an Entry
instance entry, assuming appropriate instances of Entry and Blog
are already saved to the database (so we can retrieve them below):
>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
Updating a ManyToManyField works a little
differently – use the
add() method on the field
to add a record to the relation. This example adds the Author instance
joe to the entry object:
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
To add multiple records to a ManyToManyField in one
go, include multiple arguments in the call to
add(), like this:
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
Django вызовет исключение, если вы попытаетесь добавить объект неверного типа.
Получение объектов¶
Для получения объектов из базы данных, создается QuerySet через Manager модели.
QuerySet представляет выборку объектов из базы данных. Он может не содержать, или содержать один или несколько фильтров – критерии для ограничения выборки по определенным параметрам. В терминах SQL, QuerySet - это оператор SELECT, а фильтры - условия такие, как WHERE или LIMIT.
You get a QuerySet by using your model’s
Manager. Each model has at least one
Manager, and it’s called
objects by default. Access it directly via the
model class, like so:
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
Примечание
Обратиться к менеджерам можно только через модель и нельзя через ее экземпляр. Это сделано для разделения «table-level» операций и «record-level» операций.
Manager - главный источник QuerySet для модели. Например, Blog.objects.all() вернет QuerySet, который содержит все объекты Blog из базы данных.
Получение всех объектов¶
The simplest way to retrieve objects from a table is to get all of them. To do
this, use the all() method on a
Manager:
>>> all_entries = Entry.objects.all()
Метод all() возвращает QuerySet всех объектов в базе данных.
Получение объектов через фильтры¶
QuerySet, возвращенный Manager, описывает все объекты в таблице базы данных. Обычно вам нужно выбрать только подмножество всех объектов.
Для создания такого подмножества, вы можете изменить QuerySet, добавив условия фильтрации. Два самых простых метода изменить QuerySet - это:
filter(**kwargs)Возвращает новый
QuerySet, который содержит объекты удовлетворяющие параметрам фильтрации.exclude(**kwargs)Возвращает новый
QuerySetсодержащий объекты, которые не удовлетворяют параметрам фильтрации.
Параметры фильтрации (**kwargs в определении функций выше) должны быть в формате описанном в разделе Field lookups.
Например, для создания QuerySet чтобы получить записи с 2006, используйте filter() таким образом:
Entry.objects.filter(pub_date__year=2006)
Это аналогично:
Entry.objects.all().filter(pub_date__year=2006)
Цепочка фильтров¶
The result of refining a QuerySet is itself a
QuerySet, so it’s possible to chain
refinements together. For example:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime.date(2005, 1, 30)
... )
В этом примере к начальному QuerySet, который возвращает все объекты, добавляется фильтр, затем исключающий фильтр, и еще один фильтр. Полученный QuerySet содержит все объекты, у которых заголовок начинается с «What», и которые были опубликованы между 30-го января 2005 и текущей датой.
Отфильтрованный QuerySet – уникален¶
После каждого изменения QuerySet, вы получаете новый QuerySet, который никак не связан с предыдущим QuerySet. Каждый раз создается отдельный QuerySet, который может быть сохранен и использован.
Пример:
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
Эти три QuerySets независимы. Первый – это базовый QuerySet, который содержит все объекты с заголовками, которые начинаются с «What». Второй – это множество первых с дополнительным критерием фильтрации, который исключает объекты с pub_date больше, чем текущая дата. Третий – это множество первого, с отфильтрованными объектами, у которых pub_date больше, чем текущая дата. Первоначальный QuerySet (q1) не изменяется последующим добавлением фильтров.
QuerySets – ленивы¶
QuerySets are lazy – the act of creating a
QuerySet doesn’t involve any database
activity. You can stack filters together all day long, and Django won’t
actually run the query until the QuerySet is
evaluated. Take a look at this example:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
Глядя на это можно подумать что было выполнено три запроса в базу данных. На самом деле был выполнен один запрос, в последней строке (print(q)). Результат QuerySet не будет получен из базы данных, пока вы не «попросите» об этом. Когда вы делаете это, QuerySet вычисляется запросом к базе данных. Для подробностей, в какой момент выполняется запрос, смотрите Когда вычисляется QuerySets.
Получение одного объекта с помощью get¶
filter() всегда возвращает QuerySet, даже если только один объект возвращен запросом - в этом случае, это будет QuerySet содержащий один объект.
If you know there is only one object that matches your query, you can use the
get() method on a
Manager which returns the object directly:
>>> one_entry = Entry.objects.get(pk=1)
Вы можете использовать для get() аргументы, такие же, как и для filter() - смотрите Field lookups далее.
Учтите, что есть разница между использованием get() и filter() с [0]. Если результат пустой, get() вызовет исключение DoesNotExist. Это исключение является атрибутом модели, для которой выполняется запрос. Если в примере выше не существует объекта Entry с первичным ключом равным 1, Django вызовет исключение Entry.DoesNotExist.
Также Django отреагирует, если запрос get() вернет не один объект. В этом случае будет вызвано исключение MultipleObjectsReturned, которое также является атрибутом класса модели.
Другие методы QuerySet¶
В большинстве случаев вы будете использовать all(), get(), filter() и exclude() для получения объектов из базы данных. Однако это не все доступные возможности; смотрите документацию о QuerySet API для получения информации о всех существующих методах QuerySet.
Ограничение выборки¶
Используйте синтаксис срезов для списков Python для ограничения результата выборки QuerySet. Это эквивалент таких операторов SQL как LIMIT и OFFSET.
For example, this returns the first 5 objects (LIMIT 5):
>>> Entry.objects.all()[:5]
This returns the sixth through tenth objects (OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
Отрицательные индексы (например, Entry.objects.all()[-1]) не поддерживаются.
Generally, slicing a QuerySet returns a new
QuerySet – it doesn’t evaluate the query. An
exception is if you use the «step» parameter of Python slice syntax. For
example, this would actually execute the query in order to return a list of
every second object of the first 10:
>>> Entry.objects.all()[:10:2]
Дальнейшая фильтрация или упорядочивание срезанного набора запросов запрещены из-за неоднозначного характера того, как это может работать.
To retrieve a single object rather than a list
(e.g. SELECT foo FROM bar LIMIT 1), use an index instead of a slice. For
example, this returns the first Entry in the database, after ordering
entries alphabetically by headline:
>>> Entry.objects.order_by('headline')[0]
This is roughly equivalent to:
>>> Entry.objects.order_by('headline')[0:1].get()
Заметим, что первый пример вызовет IndexError, в то время как второй - DoesNotExist, если запрос не вернёт ни одного объекта. Смотрите get() для подробностей.
Фильтры полей¶
Фильтры полей – это «операторы» для составления условий SQL WHERE. Они задаются как именованные аргументы для метода filter(), exclude() и get() в QuerySet.
Basic lookups keyword arguments take the form field__lookuptype=value.
(That’s a double-underscore). For example:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
будет транслировано в SQL:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
Как это работает
Python позволяет определить функции, которые принимают именованные аргументы с динамически вычисляемыми названиями и значениями. Подробности смотрите в разделе Именованные аргументы в официальной документации Python.
Поля указанные при фильтрации должны быть полями модели. Есть одно исключение, для поля ForeignKey можно указать поле с суффиксом _id. В этом случае необходимо передать значение первичного ключа связанной модели. Например:
>>> Entry.objects.filter(blog_id=4)
При передаче неверного именованного аргумента, будет вызвано исключение TypeError.
API базы данных поддерживает около двух дюжин фильтров; полный список можно найти в разделе о фильтрах полей. Вот пример самых используемых фильтров:
exactAn «exact» match. For example:
>>> Entry.objects.get(headline__exact="Cat bites dog")
Создаст такой SQL запрос:
SELECT ... WHERE headline = 'Cat bites dog';
Если вы не указали фильтр – именованный аргумент не содержит двойное подчеркивание – будет использован фильтр
exact.For example, the following two statements are equivalent:
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
Это сделано для удобства, т.к.
exactсамый распространенный фильтр.iexactA case-insensitive match. So, the query:
>>> Blog.objects.get(name__iexact="beatles blog")
Найдет
Blogс названием"Beatles Blog","beatles blog", и даже"BeAtlES blOG".exactРегистрозависимая проверка на вхождение. Например:
Entry.objects.get(headline__contains='Lennon')
Будет конвертировано в такой SQL запрос:
SELECT ... WHERE headline LIKE '%Lennon%';
Заметим, что этот пример найдет заголовок
'Today Lennon honored', но не найдет'today lennon honored'.Существуют также регистронезависимые версии,
icontains.startswith,endswithПоиск по началу и окончанию соответственно. Существуют также регистронезависимые версии
istartswithиiendswith.
Это только основные фильтры. Полный список ищите в разделе о фильтрах по полям.
Фильтры по связанным объектам¶
Django предлагает удобный и понятный интерфейс для фильтрации по связанным объектам, самостоятельно заботясь о JOIN в SQL. Для фильтра по полю из связанных моделей, используйте имена связывающих полей разделенных двойным нижним подчеркиванием, пока вы не достигните нужного поля.
This example retrieves all Entry objects with a Blog whose name
is 'Beatles Blog':
>>> Entry.objects.filter(blog__name='Beatles Blog')
Этот поиск может быть столь глубоким, как вам будет угодно.
It works backwards, too. Whilst it can be customized, by default you refer to a «reverse»
relationship in a lookup using the lowercase name of the model.
This example retrieves all Blog objects which have at least one Entry
whose headline contains 'Lennon':
>>> Blog.objects.filter(entry__headline__contains='Lennon')
Если вы используйте фильтр через несколько связей и одна из промежуточных моделей не содержит подходящей связи, Django расценит это как пустое значение (все значения равны NULL). Исключение не будет вызвано. Например, в этом фильтре:
Blog.objects.filter(entry__authors__name='Lennon')
(при связанной модели Author), если нет объекта author связанного с entry, это будет расценено как отсутствие name, вместо вызова исключения т.к. author отсутствует. В большинстве случаев это то, что вам нужно. Единственный случай, когда это может работать не однозначно - при использовании isnull. Например:
Blog.objects.filter(entry__authors__name__isnull=True)
вернет объекты Blog у которого пустое поле name у author и также объекты, у которых пустой author``в ``entry. Если вы не хотите включать вторые объекты, используйте:
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
Фильтрация по связям многие-ко-многим¶
When you are filtering an object based on a
ManyToManyField or a reverse
ForeignKey, there are two different sorts of filter
you may be interested in. Consider the Blog/Entry relationship
(Blog to Entry is a one-to-many relation). We might be interested in
finding blogs that have an entry which has both «Lennon» in the headline and
was published in 2008. Or we might want to find blogs that have an entry with
«Lennon» in the headline as well as an entry that was published
in 2008. Since there are multiple entries associated with a single Blog,
both of these queries are possible and make sense in some situations.
The same type of situation arises with a
ManyToManyField. For example, if an Entry has a
ManyToManyField called tags, we might want to
find entries linked to tags called «music» and «bands» or we might want an
entry that contains a tag with a name of «music» and a status of «public».
To handle both of these situations, Django has a consistent way of processing
filter() calls. Everything inside a
single filter() call is applied
simultaneously to filter out items matching all those requirements. Successive
filter() calls further restrict the set
of objects, but for multi-valued relations, they apply to any object linked to
the primary model, not necessarily those objects that were selected by an
earlier filter() call.
That may sound a bit confusing, so hopefully an example will clarify. To select all blogs that contain entries with both «Lennon» in the headline and that were published in 2008 (the same entry satisfying both conditions), we would write:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
To select all blogs that contain an entry with «Lennon» in the headline as well as an entry that was published in 2008, we would write:
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
Suppose there is only one blog that had both entries containing «Lennon» and entries from 2008, but that none of the entries from 2008 contained «Lennon». The first query would not return any blogs, but the second query would return that one blog.
In the second example, the first filter restricts the queryset to all those
blogs linked to entries with «Lennon» in the headline. The second filter
restricts the set of blogs further to those that are also linked to entries
that were published in 2008. The entries selected by the second filter may or
may not be the same as the entries in the first filter. We are filtering the
Blog items with each filter statement, not the Entry items.
Примечание
Поведение exclude() при запросе, который использует множественную связь, отличается от аналогичных запросов с filter(), поведение которых описано выше. Несколько условий в одном вызове exclude() не обязательно будут применяться к одной записи.
Например, следующий запрос исключит блоги, с записями, у которых заголовок содержит «Lennon», а также с записями опубликованными в 2008:
Blog.objects.exclude(
entry__headline__contains='Lennon',
entry__pub_date__year=2008,
)
Однако, в отличии от filter(), этот запрос не отфильтрует блоги по записям, которые удовлетворяют двум условиям. Для того, чтобы выбрать все блоги, которые не содержат записи с «Lennon» и опубликованные в 2008, необходимо сделать два запроса:
Blog.objects.exclude(
entry__in=Entry.objects.filter(
headline__contains='Lennon',
pub_date__year=2008,
),
)
Фильтры могут ссылаться на поля модели¶
В примерах выше мы использовали фильтры, которые сравнивали поля с определенными значениями(константами). Но что, если вы хотите сравнить одно поле с другим полем одной модели?
Django предоставляет класс F для таких сравнений. Экземпляр F() рассматривается как ссылка на другое поле модели. Эти ссылки могут быть использованы для сравнения значений двух разных полей одного объекта модели.
For example, to find a list of all blog entries that have had more comments
than pingbacks, we construct an F() object to reference the pingback count,
and use that F() object in the query:
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
Django supports the use of addition, subtraction, multiplication,
division, modulo, and power arithmetic with F() objects, both with constants
and with other F() objects. To find all the blog entries with more than
twice as many comments as pingbacks, we modify the query:
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)
To find all the entries where the rating of the entry is less than the sum of the pingback count and comment count, we would issue the query:
>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))
You can also use the double underscore notation to span relationships in
an F() object. An F() object with a double underscore will introduce
any joins needed to access the related object. For example, to retrieve all
the entries where the author’s name is the same as the blog name, we could
issue the query:
>>> Entry.objects.filter(authors__name=F('blog__name'))
For date and date/time fields, you can add or subtract a
timedelta object. The following would return all entries
that were modified more than 3 days after they were published:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
The F() objects support bitwise operations by .bitand(), .bitor(),
.bitxor(), .bitrightshift(), and .bitleftshift(). For example:
>>> F('somefield').bitand(16)
Оракул
Oracle не поддерживает побитовую операцию XOR.
Support for .bitxor() was added.
«Shortcut» для фильтрации по первичному ключу¶
Для удобства, Django предоставляет специальный фильтр pk для работы с первичным ключом.
In the example Blog model, the primary key is the id field, so these
three statements are equivalent:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
The use of pk isn’t limited to __exact queries – any query term
can be combined with pk to perform a query on the primary key of a model:
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)
pk lookups also work across joins. For example, these three statements are
equivalent:
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3) # __exact is implied
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
Экранирование знака процента и нижнего подчеркивания для оператора LIKE¶
Фильтры, эквивалентные оператору LIKE в SQL(iexact, contains, icontains, startswith, istartswith, endswith и iendswith), автоматически экранируют два символа, используемых оператором LIKE – знак процента и нижнего подчеркивания. (В операторе LIKE, знак процента означает «wildcard» из нескольких символов, нижнего подчеркивания - односимвольный «wildcard».)
This means things should work intuitively, so the abstraction doesn’t leak. For example, to retrieve all the entries that contain a percent sign, use the percent sign as any other character:
>>> Entry.objects.filter(headline__contains='%')
Django самостоятельно позаботится об экранировании; полученный SQL будет выглядеть приблизительно вот так:
SELECT ... WHERE headline LIKE '%\%%';
Также работает и символ нижнего подчеркивания. Оба, знак процента и нижнего подчеркивания, обрабатываются автоматически, прозрачно для вас.
Кэширование и QuerySets¶
Каждый QuerySet содержит кэш, для уменьшения количества запросов. Очень важно знать как он работает для эффективного использования Django.
В только что созданном QuerySet кеш пустой. После вычисления QuerySet и будет выполнен запрос к базе данных – Django сохраняет результат запроса в кеше QuerySet и возвращает необходимый результат (например, следующий элемент при итерации по QuerySet). Последующие вычисления QuerySet используют кеш.
Keep this caching behavior in mind, because it may bite you if you don’t use
your QuerySets correctly. For example, the
following will create two QuerySets, evaluate
them, and throw them away:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
Это означает, что один и тот же запрос будет выполнен дважды, удваивая нагрузку на базу данных. Также, есть вероятность, что списки могут содержать разные результаты, потому что запись Entry может быть добавлена или удалена в доли секунды между запросами.
To avoid this problem, save the QuerySet and
reuse it:
>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
Когда queryset не кэшируется¶
Queryset не всегда кэширует результаты. При выполнении только части queryset-а, кэш проверяется, но если кэш пустой, выполняется запрос без сохранения его результата в кэш. Это значит, что ограничение выборки, используя индекс или срез, как при использовании списков, не заполнит кэш.
For example, repeatedly getting a certain index in a queryset object will query the database each time:
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
However, if the entire queryset has already been evaluated, the cache will be checked instead:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache
Here are some examples of other actions that will result in the entire queryset being evaluated and therefore populate the cache:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
Примечание
Использование print с queryset не заполнит кэш т.к. будет вызван __repr__(), который показывает только часть объектов.
Запрос JSONField¶
Реализация поиска в JSONField отличается, в основном из-за наличия ключевых преобразований. Для демонстрации мы будем использовать следующий пример модели:
from django.db import models
class Dog(models.Model):
name = models.CharField(max_length=200)
data = models.JSONField(null=True)
def __str__(self):
return self.name
Хранение и запрос None¶
As with other fields, storing None as the field’s value will store it as
SQL NULL. While not recommended, it is possible to store JSON scalar
null instead of SQL NULL by using Value('null').
Какое бы из значений ни сохранялось, при извлечении из базы данных Python-представление скаляра JSON null совпадает с SQL NULL, т. е. None. Поэтому отличить их может быть сложно.
Это применимо только к значению None в качестве значения верхнего уровня поля. Если None находится внутри list или dict, он всегда будет интерпретироваться как JSON null.
When querying, None value will always be interpreted as JSON null. To
query for SQL NULL, use isnull:
>>> Dog.objects.create(name='Max', data=None) # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name='Archie', data=Value('null')) # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value('null'))
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data__isnull=True)
<QuerySet [<Dog: Max>]>
>>> Dog.objects.filter(data__isnull=False)
<QuerySet [<Dog: Archie>]>
Если вы не уверены, что хотите работать со значениями SQL NULL, рассмотрите возможность установки null=False и предоставления подходящего значения по умолчанию для пустых значений, например default=dict.
Примечание
Сохранение скаляра JSON null не нарушает null=False.
Преобразования ключей, индексов и путей¶
To query based on a given dictionary key, use that key as the lookup name:
>>> Dog.objects.create(name='Rufus', data={
... 'breed': 'labrador',
... 'owner': {
... 'name': 'Bob',
... 'other_pets': [{
... 'name': 'Fishy',
... }],
... },
... })
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
<Dog: Meg>
>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>
Multiple keys can be chained together to form a path lookup:
>>> Dog.objects.filter(data__owner__name='Bob')
<QuerySet [<Dog: Rufus>]>
If the key is an integer, it will be interpreted as an index transform in an array:
>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
<QuerySet [<Dog: Rufus>]>
Если ключ, который вы хотите запросить, конфликтует с именем другого поиска, используйте вместо этого поиск contains.
To query for missing keys, use the isnull lookup:
>>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
<Dog: Shep>
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
Примечание
В приведенных выше примерах поиска неявно используется поиск exact. Преобразования ключа, индекса и пути также могут быть связаны с помощью: icontains, endswith, iendswith, iexact, regex, iregex, startswith, istartswith, lt, lte, gt и gte, а также с помощью Сдерживание и поиск ключей.
Примечание
Из-за особенностей работы запросов по ключевому пути exclude() и filter() не гарантируют создание исчерпывающих наборов. Если вы хотите включить объекты, у которых нет пути, добавьте поиск isnull.
Предупреждение
Поскольку любая строка может быть ключом в объекте JSON, любой поиск, кроме перечисленных ниже, будет интерпретироваться как поиск ключа. Никаких ошибок не возникает. Будьте особенно осторожны с ошибками при вводе и всегда проверяйте, работают ли ваши запросы так, как вы задумали.
Пользователи MariaDB и Oracle
Использование order_by() для преобразований ключа, индекса или пути позволит отсортировать объекты, используя строковое представление значений. Это связано с тем, что MariaDB и база данных Oracle не предоставляют функцию, которая преобразует значения JSON в их эквивалентные значения SQL.
Пользователи Oracle
В базе данных Oracle использование None в качестве значения поиска в запросе exclude() вернет объекты, которые не имеют значения null по заданному пути, включая объекты, у которых нет пути. В других базах данных запрос вернет объекты, путь которых имеет значение, отличное от null.
Пользователи PostgreSQL
В PostgreSQL, если используется только один ключ или индекс, используется оператор SQL ->. Если используется несколько операторов, то используется оператор #>.
Сдерживание и поиск ключей¶
содержит¶
The contains lookup is overridden on JSONField. The returned
objects are those where the given dict of key-value pairs are all
contained in the top-level of the field. For example:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contains={'owner': 'Bob'})
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
>>> Dog.objects.filter(data__contains={'breed': 'collie'})
<QuerySet [<Dog: Meg>]>
Oracle и SQLite
contains не поддерживается в Oracle и SQLite.
contained_by¶
This is the inverse of the contains lookup - the
objects returned will be those where the key-value pairs on the object are a
subset of those in the value passed. For example:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})
<QuerySet [<Dog: Meg>, <Dog: Fred>]>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
<QuerySet [<Dog: Fred>]>
Oracle и SQLite
contained_by не поддерживается в Oracle и SQLite.
has_key¶
Returns objects where the given key is in the top-level of the data. For example:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_key='owner')
<QuerySet [<Dog: Meg>]>
has_keys¶
Returns objects where all of the given keys are in the top-level of the data. For example:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_keys=['breed', 'owner'])
<QuerySet [<Dog: Meg>]>
has_any_keys¶
Returns objects where any of the given keys are in the top-level of the data. For example:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_any_keys=['owner', 'breed'])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
Сложные запросы с помощью объектов Q¶
Именованные аргументы функции filter() и др. – объединяются оператором «AND». Если вам нужны более сложные запросы (например, запросы с оператором OR), вы можете использовать объекты Q.
Объект Q (django.db.models.Q) – объект, используемый для инкапсуляции множества именованных аргументов для фильтрации. Аргументы определяются так же, как и в примерах выше.
Например, этот объект Q определяет запрос LIKE:
from django.db.models import Q
Q(question__startswith='What')
Q objects can be combined using the & and | operators. When an
operator is used on two Q objects, it yields a new Q object.
Например, это определение представляет объект Q, который представляет операцию «OR» двух фильтров с "question__startswith":
Q(question__startswith='Who') | Q(question__startswith='What')
This is equivalent to the following SQL WHERE clause:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
You can compose statements of arbitrary complexity by combining Q objects
with the & and | operators and use parenthetical grouping. Also, Q
objects can be negated using the ~ operator, allowing for combined lookups
that combine both a normal query and a negated (NOT) query:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
Каждый метод для фильтрации, который принимает именованные аргументы (например, filter(), exclude(), get()get()) также может принимать объекты Q. Если вы передадите несколько объектов Q как аргументы, они будут объединены оператором «AND». Например:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
… примерно переводится в SQL:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
Вы можете использовать одновременно объекты Q и именованные аргументы. Все аргументы(будь то именованные аргументы или объекты Q) объединяются оператором «AND». Однако, если присутствует объект Q, он должен следовать перед именованными аргументами. Например:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who',
)
… правильный запрос, идентичный предыдущему примеру; но:
# INVALID QUERY
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
… будет неправильный`(Вообще Django здесь не причем. Синтаксис Python не позволяет передавать именованные аргументы перед позиционными – прим. переводчика)`.
См.также
Вы можете найти примеры использования OR с Q.
Сравнение объектов¶
Для сравнения двух экземпляров модели, используйте стандартный оператор сравнения Python, двойной знак равно: ==. При этом будут сравнены первичные ключи.
Using the Entry example above, the following two statements are equivalent:
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
If a model’s primary key isn’t called id, no problem. Comparisons will
always use the primary key, whatever it’s called. For example, if a model’s
primary key field is called name, these two statements are equivalent:
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
Удаление объектов¶
The delete method, conveniently, is named
delete(). This method immediately deletes the
object and returns the number of objects deleted and a dictionary with
the number of deletions per object type. Example:
>>> e.delete()
(1, {'weblog.Entry': 1})
Можно также удалить несколько объектов сразу. Каждый QuerySet имеет метод delete(), который удаляет все объекты из QuerySet.
For example, this deletes all Entry objects with a pub_date year of
2005:
>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})
Учтите, что при любой возможности будет использован непосредственно SQL запрос, то есть метод delete() объекта может и не использоваться при удалении. Если вы переопределяете метод delete() модели и хотите быть уверенным, что он будет вызван, вы должны «самостоятельно» удалить объект модели (например, использовать цикл по QuerySet и вызывать метод delete() для каждого объекта) не используя метод delete() QuerySet.
При удалении Django повторяет поведение SQL выражения ON DELETE CASCADE – другими словами, каждый объект, имеющий связь(ForeignKey) с удаляемым объектом, будет также удален. Например:
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
Это поведение можно изменить, определив аргумент on_delete поля ForeignKey.
Метод delete() содержится только в QuerySet и не существует в Manager. Это сделано, чтобы вы случайно не выполнили Entry.objects.delete(), и не удалили все записи. Если вы на самом деле хотите удалить все объекты, сначала явно получите QuerySet, содержащий все записи:
Entry.objects.all().delete()
Копирование объекта модели¶
Although there is no built-in method for copying model instances, it is
possible to easily create new instance with all fields“ values copied. In the
simplest case, you can set pk to None. Using our blog example:
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1
blog.pk = None
blog.save() # blog.pk == 2
Все немного сложнее, если вы используете наследование. Рассмотрим подкласс Blog:
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)
django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3
Due to how inheritance works, you have to set both pk and id to
None:
django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4
Этот процесс не копирует отношения, которые не являются частью таблицы базы данных модели. Например, у Entry есть ManyToManyField для Author. После дублирования записи необходимо установить отношения «многие ко многим» для новой записи:
entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)
Для OneToOneField вы должны дублировать связанный объект и назначить его полю нового объекта, чтобы избежать нарушения ограничения уникальности «один к одному». Например, предположим, что запись уже дублируется, как указано выше:
detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()
Изменение нескольких объектов¶
Если вам понадобиться установить значение поля для всех объектов в QuerySet, используйте метод update(). Например:
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
You can only set non-relation fields and ForeignKey
fields using this method. To update a non-relation field, provide the new value
as a constant. To update ForeignKey fields, set the
new value to be the new model instance you want to point to. For example:
>>> b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)
The update() method is applied instantly and returns the number of rows
matched by the query (which may not be equal to the number of rows updated if
some rows already have the new value). The only restriction on the
QuerySet being updated is that it can only
access one database table: the model’s main table. You can filter based on
related fields, but you can only update columns in the model’s main
table. Example:
>>> b = Blog.objects.get(pk=1)
# Update all the headlines belonging to this Blog.
>>> Entry.objects.filter(blog=b).update(headline='Everything is the same')
Учтите, что метод update() использует непосредственно SQL запрос. Это операция для массового изменения. Метод save() модели не будет вызван, сигналы pre_save или post_save не будут вызваны (которые являются следствием вызова save()), аргумент auto_now не будет учтен. Если вы хотите сохранить каждый объект в QuerySet и удостовериться что метод save() вызван для каждого объекта, вы не должны использовать какой-либо специальный метод. Просто используйте цикл и вызовите метод save():
for item in my_queryset:
item.save()
Calls to update can also use F expressions to
update one field based on the value of another field in the model. This is
especially useful for incrementing counters based upon their current value. For
example, to increment the pingback count for every entry in the blog:
>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)
However, unlike F() objects in filter and exclude clauses, you can’t
introduce joins when you use F() objects in an update – you can only
reference fields local to the model being updated. If you attempt to introduce
a join with an F() object, a FieldError will be raised:
# This will raise a FieldError
>>> Entry.objects.update(headline=F('blog__name'))
Использование чистого SQL¶
Если вам нужно создать SQL запрос, который слишком сложен для API Django, вы можете использовать чистый SQL. Django имеет несколько возможностей использовать SQL запросы; смотрите Использование чистого SQL.
Наконец, важно отметить, что API Django для работы с базой данных является лишь интерфейсом к базе данных. Вы можете получить доступ к базе данных через другие инструменты, языки программирования и фреймворки; Django не делает ничего специфического с вашей базой данных.