Создание запросов¶
После того как вы создали модели данных, Django автоматически предоставляет вам API-интерфейс для базы данных, который позволяет создавать, извлекать, обновлять и удалять объекты. Этот документ объясняет, как использовать этот API. Обратитесь к справочнику по модели данных для получения полной информации обо всех различных параметрах поиска модели.
В этом руководстве (и в справочнике) мы будем ссылаться на следующие модели, из которых состоит приложение для блога:
from datetime import date
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(default=date.today)
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField(default=0)
number_of_pingbacks = models.IntegerField(default=0)
rating = models.IntegerField(default=5)
def __str__(self):
return self.headline
Создание объектов¶
Для представления данных таблицы базы данных в объектах Python Django использует интуитивно понятную систему: класс модели представляет таблицу базы данных, а экземпляр этого класса представляет конкретную запись в таблице базы данных.
Чтобы создать объект, создайте его экземпляр с помощью аргументов ключевого слова для класса модели, а затем вызовите save()
, чтобы сохранить его в базе данных.
Если предположить, что модели живут в файле mysite/blog/models.py
, то вот пример:
>>> from blog.models import Blog
>>> b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
>>> b.save()
Выполняется SQL-оператор INSERT
за кулисами. Django не обращается в базу данных, пока вы не вызовете явно save()
.
Метод save()
не имеет возвращаемого значения.
Сохранение изменений в объектах¶
Чтобы сохранить изменения в объекте, который уже находится в базе данных, используйте save()
.
Для экземпляра Blog
b5
, который уже был сохранен в базе данных, в этом примере изменяется его имя и обновляется запись о нем в базе данных:
>>> b5.name = "New name"
>>> b5.save()
Здесь выполняется оператор SQL UPDATE
. Django не делает запрос в базу данных, пока вы не вызовете явно save()
.
Сохранение полей ForeignKey
и ManyToManyField
¶
Обновление поля ForeignKey
происходит точно так же, как и сохранение обычного поля - присваивается объект нужного типа данному полю. В данном примере обновляется атрибут blog
экземпляра Entry
entry
, при этом предполагается, что соответствующие экземпляры Entry
и Blog
уже сохранены в базе данных (чтобы мы могли их извлечь ниже):
>>> 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()
Обновление ManyToManyField
работает несколько иначе - для добавления записи в отношение используйте метод add()
для поля. В данном примере к объекту entry
добавляется экземпляр Author
joe
:
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
Чтобы добавить несколько записей в ManyToManyField
за один раз, включите несколько аргументов в вызов add()
, например, так:
>>> 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)
Джанго сообщит, если вы попытаетесь назначить или добавить объект неправильного типа.
Получение объектов¶
Чтобы получить объекты из вашей базы данных, создайте QuerySet
через Manager
в своем классе модели.
QuerySet
представляет коллекцию объектов из вашей базы данных. Может иметь ноль, один или несколько фильтров. Фильтры сужают результаты запроса на основе заданных параметров. В терминах SQL QuerySet
приравнивается к оператору SELECT
, а фильтр является ограничивающим предложением, таким как WHERE
или LIMIT
.
Вы получаете QuerySet
, используя Manager
своей модели. Каждая модель имеет по крайней мере одну Manager
, и по умолчанию она называется objects
. Доступ к нему осуществляется непосредственно через класс модели, например, так:
>>> 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."
Примечание
Managers
доступны только через классы модели, а не из экземпляров модели, чтобы обеспечить разделение между операциями на уровне таблицы и операциями на уровне записи.
Manager
является основным источником QuerySets
для модели. Например, Blog.objects.all() `` возвращает :class:`~django.db.models.query.QuerySet`, который содержит все объекты ``Blog
в базе данных.
Получение всех объектов¶
Самый простой способ извлечь объекты из таблицы - получить их все. Для этого следует использовать метод all()
на Manager
:
>>> all_entries = Entry.objects.all()
Метод all()
возвращает QuerySet
всех объектов в базе данных.
Получение определенных объектов с помощью фильтров¶
QuerySet
, возвращаемый all()
описывает все объекты в таблице базы данных. Обычно нужно выбрать только подмножество полного набора объектов.
Чтобы создать такое подмножество, вы уточняете исходный QuerySet
, добавляя условия фильтра. Два наиболее распространенных способа уточнить QuerySet
:
filter(**kwargs)
- Возвращает новый
QuerySet
, содержащий объекты, которые соответствуют заданным параметрам поиска. exclude(**kwargs)
- Возвращает новый
QuerySet
, содержащий объекты, которые не соответствуют указанным параметрам поиска.
Параметры поиска (** kwargs
в приведенных выше определениях функций) должны быть в формате, описанном в разделе «Поиск в поле» ниже.
Например, чтобы получить QuerySet
записей блога за 2006 год, используйте filter()
примерно так:
Entry.objects.filter(pub_date__year=2006)
С классом менеджера по умолчанию, он такой же, как:
Entry.objects.all().filter(pub_date__year=2006)
Цепочки фильтров¶
Результат уточнения QuerySet
сам является QuerySet
, поэтому возможна цепочка уточнений. Например:
>>> 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
) не зависит от процесса уточнения.
`` QuerySet`` - ленивые¶
QuerySets
являются ленивыми - акт создания QuerySet
не включает в себя никакой активности базы данных. Вы можете накладывать фильтры друг на друга хоть целыми днями, и Django не будет выполнять запрос до тех пор, пока QuerySet
не будет оценен. Взгляните на этот пример:
>>> 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
вызывается путем доступа к базе данных. Для получения более подробной информации о том, когда именно происходит вызов, смотрите Когда выполняются QuerySet.
Получение одного объекта с помощью get()
¶
filter()
всегда возвращает QuerySet
, даже если только один объект соответствует запросу - в этом случае это будет QuerySet
, содержащий один элемент.
Если вы знаете, что существует только один объект, соответствующий вашему запросу, вы можете использовать метод get()
на Manager
, который возвращает объект напрямую:
>>> one_entry = Entry.objects.get(pk=1)
Вы можете использовать любое выражение запроса с get()
, так же как с filter()
. Смотрите «Поиск по полю» ниже.
Обратите внимание, что есть разница между использованием get()
и использованием filter()
с фрагментом [0]
. Если нет результатов, соответствующих запросу, то get()
вызовет исключение DoesNotExist
. Это исключение является атрибутом класса модели, к которому выполняется запрос - поэтому в приведенном выше коде, если нет объекта Entry
с первичным ключом 1, Django вызовет Entry.DoesNotExist
.
Точно так же Django выдаст ошибку, если более чем один элемент соответствует запросу get()
. В этом случае он вызовет MultipleObjectsReturned
, который тоже является атрибутом самого класса модели.
Другие методы QuerySet
¶
В большинстве случаев вы будете использовать all()
, get()
, filter()
и exclude()
, когда вам нужно искать объекты в базе данных. Однако это далеко не все; смотрите Справочник по API QuerySet для получения полного списка всех методов QuerySet
.
Ограничение QuerySet
¶
Используйте подмножество синтаксиса нарезки массивов в Python, чтобы ограничить ваш QuerySet
определенным количеством результатов. Это эквивалент выражений SQL LIMIT
и OFFSET
.
Например, здесь возвращаются первые 5 объектов (LIMIT 5
):
>>> Entry.objects.all()[:5]
При этом возвращаются объекты с шестого по десятый (OFFSET 5 LIMIT 5
):
>>> Entry.objects.all()[5:10]
Отрицательное индексирование (т.е. Entry.objects.all()[-1]
) не поддерживается.
В общем случае срез QuerySet
возвращает новый QuerySet
- он не оценивает запрос. Исключением является использование параметра «step» в синтаксисе Python slice. Например, в этом случае будет фактически выполнен запрос, чтобы вернуть список каждого второго объекта из первых 10:
>>> Entry.objects.all()[:10:2]
Дальнейшая фильтрация или упорядочение нарезанного набора запросов запрещены из-за неоднозначного характера того, как это может работать.
Чтобы получить не список (например, SELECT foo FROM bar LIMIT 1
), а одиночный объект, используйте индекс вместо среза. Например, здесь возвращается первый Entry
в базе данных, после упорядочивания записей в алфавитном порядке по заголовку:
>>> Entry.objects.order_by("headline")[0]
Это примерно эквивалентно:
>>> Entry.objects.order_by("headline")[0:1].get()
Однако обратите внимание, что первый из них вызовет IndexError
, а второй вызовет DoesNotExist
, если ни один объект не соответствует заданным критериям. Смотрите get()
для получения более подробной информации.
Поиск по полям¶
Поиск по полю - это то, как вы определяете содержание выражения SQL WHERE
. Они указываются в качестве аргументов ключевых слов для методов filter()
, exclude()
и get()
класса QuerySet
.
Основные аргументы ключевого слова lookups имеют вид field__lookuptype=value
. (Это двойное подчеркивание). Например:
>>> Entry.objects.filter(pub_date__lte="2006-01-01")
переводит (примерно) в следующий SQL:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
Как это возможно
Python имеет возможность определять функции, которые принимают произвольные аргументы имя-значение, имена и значения которых оцениваются во время выполнения. Для получения дополнительной информации см. Keyword Arguments в официальном руководстве по Python.
Поле, указанное в поиске, должно быть именем поля модели. Однако есть одно исключение: в случае ForeignKey
вы можете указать имя поля с суффиксом _id
. В этом случае ожидается, что параметр value будет содержать необработанное значение первичного ключа внешней модели. Например:
>>> Entry.objects.filter(blog_id=4)
Если вы передадите неверный аргумент ключевого слова, функция поиска вызовет TypeError
.
API базы данных поддерживает около двух десятков типов поиска; полная документация может быть найдена в справочнике поиска по полям. Чтобы дать вам представление о том, что доступно, вот некоторые из наиболее часто используемых поисков:
exact
«Точное» совпадение. Например:
>>> Entry.objects.get(headline__exact="Cat bites dog")
Будет генерировать SQL по этим строкам:
SELECT ... WHERE headline = 'Cat bites dog';
Если вы не предоставляете тип поиска - то есть, если аргумент ключевого слова не содержит двойного подчеркивания, - тип поиска считается
точным
.Например, следующие два утверждения эквивалентны:
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
Это сделано для удобства, потому что
точный
поиск является распространенным случаем.iexact
Нечувствительное к регистру совпадение. Таким образом, запрос:
>>> Blog.objects.get(name__iexact="beatles blog")
Будет соответствовать
Blog
с названием"Beatles Blog"
,"beatles blog"
или даже"BeAtlES blOG"
.contains
Проверка содержимого в зависимости от регистра. Например:
Entry.objects.get(headline__contains="Lennon")
Примерно переводится в такой SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
Обратите внимание, что это будет соответствовать заголовку
'Today Lennon honored'
, но не'today lennon honored'
.Существует также версия без учета регистра
icontains
.startswith
,endswith
- «Начинается с» и «заканчивается» соответственно. Существуют также версии без учета регистра
istartswith
иiendswith
.
Опять же, это только поверхностное описание. Полное руководство можно найти в Справочнике поиска по полям.
Поиск, который использует отношения¶
Django предлагает мощный и интуитивно понятный способ «отслеживать» отношения в поисках, автоматически заботясь о SQL JOIN
для вас, за кулисами. Чтобы охватить отношение, просто используйте имя поля связанных полей в моделях, разделенных двойным подчеркиванием, пока не дойдете до нужного поля.
В данном примере извлекаются все объекты Entry
с номером Blog
, чей name
является 'Beatles Blog'
:
>>> Entry.objects.filter(blog__name="Beatles Blog")
Этот охват может быть настолько глубоким, насколько вы захотите.
Он работает и в обратном направлении. Хотя может быть настроен как
, по умолчанию вы ссылаетесь на «обратную» связь в поиске, используя имя модели в нижнем регистре.
В данном примере извлекаются все объекты Blog
, имеющие хотя бы один Entry
, чей headline
содержит 'Lennon'
:
>>> Blog.objects.filter(entry__headline__contains="Lennon")
Если вы фильтруете по нескольким отношениям и одна из промежуточных моделей не имеет значения, которое удовлетворяет условию фильтра, Django будет обрабатывать его как пустой (все значения NULL
), но допустимый объект там. Все это означает, что никакой ошибки не возникнет. Например, в этом фильтре:
Blog.objects.filter(entry__authors__name="Lennon")
(если была связанная Author
модель), если бы не было author
, связанного с записью, она будет обрабатываться так, как если бы к ней также не было прикреплено 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)
Охватывающие многозначные отношения¶
При охвате ManyToManyField
или обратном ForeignKey
(например, от Blog
к Entry
) фильтрация по нескольким атрибутам поднимает вопрос о том, нужно ли требовать, чтобы каждый атрибут совпадал в одном и том же связанном объекте. Мы можем искать блоги, в которых есть запись 2008 года с «Lennon « в заголовке, или мы можем искать блоги, в которых есть просто любая запись 2008 года, а также более новая или старая запись с «Lennon « в заголовке.
Чтобы выбрать все блоги, содержащие хотя бы одну запись за 2008 год с «Lennon « в заголовке (одна и та же запись удовлетворяет обоим условиям), мы напишем:
Blog.objects.filter(entry__headline__contains="Lennon", entry__pub_date__year=2008)
Иначе, чтобы выполнить более свободный запрос, выбирающий все блоги, в которых есть только некоторая запись с «Lennon « в заголовке и некоторая запись от 2008 года, мы бы написали:
Blog.objects.filter(entry__headline__contains="Lennon").filter(
entry__pub_date__year=2008
)
Предположим, что существует только один блог, в котором есть как записи, содержащие «Lennon «, так и записи 2008 года, но ни одна из записей 2008 года не содержит «Lennon «. Первый запрос не вернет ни одного блога, но второй запрос вернет этот единственный блог. (Это происходит потому, что записи, отобранные вторым фильтром, могут совпадать или не совпадать с записями в первом фильтре. Мы фильтруем элементы Blog
с каждым оператором фильтра, а не элементы Entry
). Короче говоря, если каждое условие должно соответствовать одному и тому же связанному объекту, то каждое из них должно содержаться в одном вызове filter()
.
Примечание
Поскольку второй (более разрешительный) запрос цепляет несколько фильтров, он выполняет несколько присоединений к первичной модели, потенциально давая дубликаты.
>>> from datetime import date
>>> beatles = Blog.objects.create(name='Beatles Blog')
>>> pop = Blog.objects.create(name='Pop Music Blog')
>>> Entry.objects.create(
... blog=beatles,
... headline='New Lennon Biography',
... pub_date=date(2008, 6, 1),
... )
<Entry: New Lennon Biography>
>>> Entry.objects.create(
... blog=beatles,
... headline='New Lennon Biography in Paperback',
... pub_date=date(2009, 6, 1),
... )
<Entry: New Lennon Biography in Paperback>
>>> Entry.objects.create(
... blog=pop,
... headline='Best Albums of 2008',
... pub_date=date(2008, 12, 15),
... )
<Entry: Best Albums of 2008>
>>> Entry.objects.create(
... blog=pop,
... headline='Lennon Would Have Loved Hip Hop',
... pub_date=date(2020, 4, 1),
... )
<Entry: Lennon Would Have Loved Hip Hop>
>>> Blog.objects.filter(
... entry__headline__contains='Lennon',
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>]>
>>> Blog.objects.filter(
... entry__headline__contains='Lennon',
... ).filter(
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>, <Blog: Beatles Blog>, <Blog: Pop Music Blog]>
Примечание
Поведение filter()
для запросов, которые охватывают многозначные отношения, как описано выше, не реализовано эквивалентно для exclude()
. Вместо этого условия в одном вызове 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()
действуют как ссылка на поле модели в запросе. Затем эти ссылки можно использовать в фильтрах запросов для сравнения значений двух разных полей в одном экземпляре модели.
Например, чтобы найти список всех записей блога, в которых комментариев было больше, чем откликов, мы создаем объект F()
для ссылки на количество откликов и используем этот объект F()
в запросе:
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks"))
Django поддерживает использование сложения, вычитания, умножения, деления, модульной и силовой арифметики с объектами F()
, как с константами, так и с другими объектами F()
. Чтобы найти все записи в блоге, в которых комментариев в два раза больше, чем пингбэков, мы модифицируем запрос:
>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks") * 2)
Чтобы найти все записи, в которых рейтинг записи меньше суммы количества пингбэков и количества комментариев, мы зададим запрос:
>>> Entry.objects.filter(rating__lt=F("number_of_comments") + F("number_of_pingbacks"))
Для разделения отношений в объекте F()
можно также использовать обозначение двойного подчеркивания. Объект F()
с двойным подчеркиванием вводит все соединения, необходимые для доступа к связанному объекту. Например, чтобы получить все записи, в которых имя автора совпадает с названием блога, можно выполнить такой запрос:
>>> Entry.objects.filter(authors__name=F("blog__name"))
Для полей даты и даты/времени можно добавить или вычесть объект timedelta
. В следующем примере будут возвращены все записи, которые были изменены более чем через 3 дня после их публикации:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F("pub_date") + timedelta(days=3))
Объекты F()
поддерживают побитовые операции .bitand()
, .bitor()
, .bitxor()
, .bitrightshift()
и .bitleftshift()
. Например:
>>> F("somefield").bitand(16)
Oracle
Oracle не поддерживает побитовую операцию XOR.
Выражения могут ссылаться на преобразования¶
Django поддерживает использование преобразований в выражениях.
Например, для поиска всех объектов Entry
, опубликованных в том же году, когда они были последний раз изменены:
>>> from django.db.models import F
>>> Entry.objects.filter(pub_date__year=F("mod_date__year"))
Чтобы найти самый ранний год публикации записи, можно задать запрос:
>>> from django.db.models import Min
>>> Entry.objects.aggregate(first_published_year=Min("pub_date__year"))
В данном примере найдено значение записи с наивысшей оценкой и общее количество комментариев ко всем записям за каждый год:
>>> from django.db.models import OuterRef, Subquery, Sum
>>> Entry.objects.values("pub_date__year").annotate(
... top_rating=Subquery(
... Entry.objects.filter(
... pub_date__year=OuterRef("pub_date__year"),
... )
... .order_by("-rating")
... .values("rating")[:1]
... ),
... total_comments=Sum("number_of_comments"),
... )
Использование сокращения pk
¶
Для удобства Django предоставляет сокращение для поиска pk
, который обозначает «первичный ключ».
В примере модели Blog
первичным ключом является поле id
, поэтому эти три утверждения эквивалентны:
>>> 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
Использование pk
не ограничивается запросами __exact
- любой термин запроса может быть объединен с pk
для выполнения запроса к первичному ключу модели:
# 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
также работают через джойны. Например, эти три утверждения эквивалентны:
>>> 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
(iexact
, contains
, icontains
, startswith
, istartswith
, endswith
and iendswith
) автоматически уберет два специальных символа, используемых в операторах LIKE
- знак процента и подчеркивание. (В операторе LIKE
знак процента означает подстановочный знак из нескольких символов, а подчеркивание означает подстановочный знак из одного символа.)
Это означает, что все должно работать интуитивно, чтобы абстракция не утекала. Например, чтобы получить все записи, содержащие знак процента, используйте знак процента как любой другой символ:
>>> Entry.objects.filter(headline__contains="%")
Джанго заботится об экранировании для вас; результирующий SQL будет выглядеть примерно так:
SELECT ... WHERE headline LIKE '%\%%';
То же самое касается подчеркивания. Оба знака процента и подчеркивания обрабатываются для вас прозрачно.
Кэширование и QuerySet
¶
Каждый класс QuerySet
содержит кэш для минимизации доступа к базе данных. Понимание того, как это работает, позволит вам написать наиболее эффективный код.
Во вновь созданном QuerySet
кеш пуст. Первый раз QuerySet
оценивается и, следовательно, происходит запрос к базе данных - Django сохраняет результаты запроса в QuerySet
и возвращает результаты, которые были явно запрошены (например, следующий элемент, если итерация происходит через QuerySet
). Последующие оценки QuerySet
повторно используют кэшированные результаты.
Помните о таком поведении кэширования, поскольку оно может подкосить вас, если вы не будете правильно использовать свои QuerySet
s. Например, в следующем примере будут созданы два QuerySet
, оценены и выброшены:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
Это означает, что один и тот же запрос к базе данных будет выполнен дважды, что фактически удваивает нагрузку на вашу базу данных. Кроме того, существует вероятность того, что два списка могут не включать в себя одни и те же записи базы данных, потому что «запись» может быть добавлена или удалена за доли секунды между двумя запросами.
Чтобы избежать этой проблемы, сохраните QuerySet
и используйте его повторно:
>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Reuse the cache from the evaluation.
Когда QuerySet
не кэшируется¶
Queryset`ы не всегда кэшируют свои результаты. При оценке только части набора запросов проверяется кэш, но если он не заполняется, элементы, возвращаемые последующим запросом, не кэшируются. В частности, это означает, что ограничение набора запросов с использованием среза массива или индекса не заполнит кэш.
Например, многократное получение определенного индекса в объекте queryset будет каждый раз запрашивать базу данных:
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
Однако если весь набор запросов уже был оценен, то вместо этого будет проверен кэш:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache
Приведем примеры других действий, которые приведут к оценке всего набора запросов и, следовательно, к заполнению кэша:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
Примечание
Простой вывод набора запросов не заполнит кэш. Это связано с тем, что вызов __repr__()
возвращает только часть всего набора запросов.
Асинхронные запросы¶
Если вы пишете асинхронные представления или код, вы не можете использовать ORM для запросов совсем так, как мы описали выше, поскольку вы не можете вызывать блокирующий синхронный код из асинхронного кода - он заблокирует цикл событий (или, что более вероятно, Django заметит и поднимет SynchronousOnlyOperation
, чтобы предотвратить это).
К счастью, вы можете выполнять многие запросы, используя API асинхронных запросов Django. Каждый метод, который может заблокироваться - например, get()
или delete()
- имеет асинхронный вариант (aget()
или adelete()
), а когда вы выполняете итерацию результатов, вы можете использовать асинхронную итерацию (async for
).
Итерация запросов¶
Стандартный способ итерации запроса - с помощью for
- приводит к блокировке запроса к базе данных за кулисами, поскольку Django загружает результаты во время итерации. Чтобы исправить это, вы можете перейти на async for
:
async for entry in Authors.objects.filter(name__startswith="A"):
...
Имейте в виду, что вы также не можете делать другие действия, которые могут привести к итерации по кверисету, например, обернуть list()
вокруг него для принудительной оценки (вы можете использовать async for
в comprehension, если вам это нужно).
Поскольку методы QuerySet
, такие как filter()
и exclude()
, фактически не выполняют запрос - они настраивают кверисет на выполнение при итерации, - вы можете свободно использовать их в асинхронном коде. О том, какие методы можно продолжать использовать подобным образом, а какие имеют асинхронные версии, читайте в следующем разделе.
QuerySet
и методы менеджера¶
Некоторые методы менеджеров и кверисетов - например, get()
и first()
- принудительно выполняют кверисет и являются блокирующими. Некоторые, например filter()
и exclude()
, не принуждают к выполнению и поэтому безопасны для запуска из асинхронного кода. Но как же отличить их друг от друга?
Хотя вы можете порыться и посмотреть, есть ли версия метода с префиксом a
(например, у нас есть aget()
, но нет afilter()
), есть более логичный способ - посмотреть, что это за метод в QuerySet reference.
В нем вы найдете методы по QuerySets, сгруппированные в два раздела:
- Методы, возвращающие новые наборы запросов: Это неблокирующие методы, у них нет асинхронных версий. Вы можете использовать их в любой ситуации, хотя перед использованием прочитайте примечания к
defer()
иonly()
. - Методы, которые не возвращают кверисетов: Это блокирующие методы, и у них есть асинхронные версии - асинхронное имя каждого из них указано в документации, хотя наш стандартный шаблон - добавление префикса
a
.
Используя это различие, вы можете определить, когда вам нужно использовать асинхронные версии, а когда нет. Например, вот правильный асинхронный запрос:
user = await User.objects.filter(username=my_input).afirst()
filter()
возвращает кверисет, и поэтому его можно продолжать передавать по цепочке в асинхронной среде, тогда как first()
оценивает и возвращает экземпляр модели - таким образом, мы переходим на afirst()
и используем await
перед всем выражением, чтобы вызвать его асинхронным способом.
Примечание
Если вы забудете вставить часть await
, вы можете увидеть ошибки типа «объект coroutine не имеет атрибута x « или «<coroutine …>» строки вместо экземпляров вашей модели. Если вы видите такие ошибки, значит, где-то не хватает await
для превращения объекта coroutine в реальное значение.
Финансовые транзакции¶
Транзакции не в настоящее время поддерживаются с асинхронными запросами и обновлениями. Вы обнаружите, что при попытке использовать одну из них возникает ошибка SynchronousOnlyOperation
.
Если вы хотите использовать транзакцию, мы предлагаем вам написать ваш код ORM в отдельной синхронной функции и затем вызвать ее с помощью sync_to_async
- см. подробнее Поддержка асинхронного режима.
Запросы к 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``¶
Как и в случае с другими полями, если в качестве значения поля записать None
, то оно будет храниться как SQL NULL
. Хотя это и не рекомендуется, можно хранить JSON-скаляр null
вместо SQL NULL
, используя Value(None, JSONField())
.
Независимо от того, какие значения сохранены, при извлечении из базы данных представление Python скалярного JSON null
совпадает с SQL NULL
, т.е. None
. Поэтому может быть трудно различить их.
Это относится только к None
как значению верхнего уровня поля. Если None
находится внутри list
или dict
, он всегда будет интерпретироваться как JSON null
.
При запросе значение None
всегда будет интерпретироваться как JSON null
. Для запроса на SQL NULL
используйте isnull
:
>>> Dog.objects.create(name="Max", data=None) # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name="Archie", data=Value(None, JSONField())) # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value(None, JSONField()))
<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
.
Добавлена поддержка выражения JSON null
с помощью Value(None, JSONField())
.
Не рекомендуется, начиная с версии 4.2: Передача Value("null")
для выражения JSON null
устарела.
Преобразование ключа, индекса и пути¶
Чтобы сделать запрос по заданному ключу словаря, используйте этот ключ в качестве имени поиска:
>>> 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>]>
Несколько ключей могут быть соединены в цепочку для формирования поиска пути:
>>> Dog.objects.filter(data__owner__name="Bob")
<QuerySet [<Dog: Rufus>]>
Если ключ является целым числом, то он будет интерпретирован как индексное преобразование в массиве:
>>> Dog.objects.filter(data__owner__other_pets__0__name="Fishy")
<QuerySet [<Dog: Rufus>]>
Если ключ, который вы хотите запросить, конфликтует с именем другого поиска, используйте вместо этого поиск contains
.
Для запроса отсутствующих ключей используйте поиск isnull
:
>>> 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
, также как с Сдерживание и ключевые поиски.
KT()
выражения¶
-
class
KT
(lookup)¶ Представляет собой текстовое значение ключа, индекса или преобразования пути
JSONField
. Для цепочки ключевых и индексных преобразований словаря можно использовать нотацию двойного подчеркивания вlookup
.Например:
>>> from django.db.models.fields.json import KT >>> Dog.objects.create( ... name="Shep", ... data={ ... "owner": {"name": "Bob"}, ... "breed": ["collie", "lhasa apso"], ... }, ... ) <Dog: Shep> >>> Dogs.objects.annotate( ... first_breed=KT("data__breed__1"), owner_name=KT("data__owner__name") ... ).filter(first_breed__startswith="lhasa", owner_name="Bob") <QuerySet [<Dog: Shep>]>
Примечание
Из-за способа работы запросов по ключевым путям exclude()
и filter()
не гарантируются производить исчерпывающие наборы. Если вы хотите включить объекты, у которых нет пути, добавьте поиск isnull
.
Предупреждение
Поскольку любая строка может быть ключом в объекте JSON, любой поиск, кроме перечисленных ниже, будет интерпретироваться как поиск ключа. Ошибок не возникает. Будьте особенно осторожны с опечатками и всегда проверяйте, работают ли ваши запросы так, как вы хотите.
Пользователи MariaDB и Oracle
Использование order_by()
для преобразований ключа, индекса или пути отсортирует объекты с использованием строкового представления значений. Это связано с тем, что MariaDB и Oracle Database не предоставляют функцию, которая преобразует значения JSON в их эквивалентные значения SQL.
Пользователи Oracle
В Oracle Database использование None
в качестве значения для поиска в запросе exclude()
вернет объекты, которые не имеют null
в качестве значения в заданный путь, включая объекты, которые не имеют пути. На других серверах базы данных запрос будет возвращать объекты, которые имеют путь, и значение не равно null
.
Пользователи PostgreSQL
В PostgreSQL, если используется только один ключ или индекс, используется оператор SQL ->
. Если используется несколько операторов, то используется оператор #>
.
Пользователи SQLite
В SQLite строковые значения "true"
, "false"
и "null"
всегда будут интерпретироваться как True
, False
и JSON null
соответственно.
Сдерживание и ключевые поиски¶
contains
¶
Поиск contains
переопределяется на JSONField
. Возвращаются объекты, в которых заданные dict
пары ключ-значение содержатся в верхнем уровне поля. Например:
>>> 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
¶
Это обратный вариант поиска contains
- возвращаться будут те объекты, в которых пары ключ-значение объекта являются подмножеством пар в переданном значении. Например:
>>> 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
contains_by
не поддерживается в Oracle и SQLite.
has_key
¶
Возвращает объекты, в которых заданный ключ находится на верхнем уровне данных. Например:
>>> 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
¶
Возвращает объекты, в которых все заданные ключи находятся на верхнем уровне данных. Например:
>>> 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
¶
Возвращает объекты, в которых любой из заданных ключей находится на верхнем уровне данных. Например:
>>> 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
можно объединять с помощью операторов &
, |
и ^
. Когда оператор используется для двух объектов Q
, получается новый объект Q
.
Например, этот оператор выдает один объект Q
, который представляет «OR» двух запросов "question__startswith"
:
Q(question__startswith="Who") | Q(question__startswith="What")
Это эквивалентно следующему предложению SQL WHERE
:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Вы можете составлять утверждения произвольной сложности, комбинируя объекты Q
с операторами &
, |
и ^
и используя скобочную группировку. Кроме того, объекты Q
можно отрицать с помощью оператора ~
, что позволяет выполнять комбинированный поиск, объединяющий обычный запрос и отрицаемый (NOT
) запрос:
Q(question__startswith="Who") | ~Q(pub_date__year=2005)
Каждая функция поиска, которая принимает ключевые слова-аргументы (например filter()
, exclude()
, 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)),
)
… не будет верным.
См.также
примеры поиска с OR в модульных тестах Django показывают некоторые возможные варианты использования Q
.
Добавлена поддержка оператора ^
(XOR
).
Сравнение объектов¶
Чтобы сравнить два экземпляра модели, просто используйте стандартный оператор сравнения Python, знак двойного равенства: ==
. За кулисами сравниваются значения первичных ключей двух моделей.
Используя приведенный выше пример Entry
, следующие два утверждения эквивалентны:
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
Если первичный ключ модели не называется id
, то проблем нет. Сравнения всегда будут использовать первичный ключ, как бы он ни назывался. Например, если поле первичного ключа модели называется name
, то эти два утверждения эквивалентны:
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
Удаление объектов¶
Метод удаления, что очень удобно, называется delete()
. Этот метод немедленно удаляет объект и возвращает количество удаленных объектов и словарь с количеством удалений по типам объектов. Пример:
>>> e.delete()
(1, {'blog.Entry': 1})
Вы также можете удалить объекты массово. Каждый метод QuerySet
имеет метод delete()
, который удаляет все элементы этого класса QuerySet
.
Например, здесь удаляются все объекты Entry
с годом pub_date
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
- другими словами, любые объекты, имеющие внешние ключи, указывающие на объект, который будет удален, будут удалены вместе с ним. Например:
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()
и удаление всех записей. Если вы хотите хотите удалить все объекты, вам нужно явно запросить полный набор запросов:
Entry.objects.all().delete()
Копирование экземпляров модели¶
Хотя нет встроенного метода для копирования экземпляров модели, можно легко создать новый экземпляр со всеми скопированными значениями полей. В простейшем случае вы можете установить для pk
значение None
, а для _state.adding
значение True
. На примере нашего блога:
blog = Blog(name="My blog", tagline="Blogging is easy")
blog.save() # blog.pk == 1
blog.pk = None
blog._state.adding = True
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
Из-за того, как работает наследование, вы должны установить для pk
и id
значение None
, а для _state.adding
значение True
:
django_blog.pk = None
django_blog.id = None
django_blog._state.adding = True
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._state.adding = True
entry.save()
entry.authors.set(old_authors)
Для OneToOneField
вы должны продублировать связанный объект и назначить его полю нового объекта, чтобы избежать нарушения однозначного уникального ограничения. Например, предполагая, что entry
уже продублировано, как указано выше:
detail = EntryDetail.objects.all()[0]
detail.pk = None
detail._state.adding = True
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")
С помощью этого метода можно устанавливать только не связанные между собой поля и поля ForeignKey
. Для обновления нереляционного поля укажите новое значение в виде константы. Для обновления полей ForeignKey
задайте в качестве нового значения новый экземпляр модели, на который вы хотите указать. Например:
>>> b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.update(blog=b)
Метод update()
применяется мгновенно и возвращает количество строк, соответствующих запросу (которое может быть не равно количеству обновленных строк, если некоторые строки уже имеют новое значение). Единственное ограничение на обновляемый QuerySet
заключается в том, что он может обращаться только к одной таблице базы данных - главной таблице модели. Вы можете фильтровать по связанным полям, но обновлять можно только столбцы главной таблицы модели. Пример:
>>> 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()
Вызовы update могут также использовать F expressions
для обновления одного поля на основе значения другого поля в модели. Это особенно удобно для увеличения счетчиков на основе их текущего значения. Например, для увеличения количества пингбэков для каждой записи в блоге:
>>> Entry.objects.update(number_of_pingbacks=F("number_of_pingbacks") + 1)
Однако, в отличие от объектов F()
в предложениях фильтрации и исключения, при использовании объектов F()
в обновлении нельзя вводить объединения - можно ссылаться только на поля, локальные для обновляемой модели. Если попытаться ввести объединение с помощью объекта F()
, то будет выдана ошибка FieldError
:
# This will raise a FieldError
>>> Entry.objects.update(headline=F("blog__name"))
Откат к сырому SQL¶
Если вам понадобится написать SQL-запрос, который слишком сложен для обработки базы данных Django, вы можете вернуться к написанию SQL вручную. У Django есть несколько вариантов написания необработанных SQL-запросов; смотрите Выполнение необработанных SQL-запросов.
Наконец, важно отметить, что уровень базы данных Django - это просто интерфейс к вашей базе данных. Вы можете получить доступ к вашей базе данных с помощью других инструментов, языков программирования или фреймворков баз данных; нет ничего специфичного для Django в вашей базе данных.