Выполнение запросов

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

В этом разделе(и последующих) мы будем использовать такие модели:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        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()

В результате выполнения этого кода будет создан INSERT SQL-запрос. Django не выполняет запросов к базе данных, пока не будет вызван метод save().

Метод save() ничего не возвращает.

См.также

save() принимает ряд аргументов, не описанных в этом разделе. Смотрите документацию о методе save() для подробностей.

Чтобы создать и сохранить объект используйте метод create().

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

Для сохранения изменений в объект, который уже существует в базе данных, используйте save().

В данном примере изменяется название объекта b5 модели Blog и обновляется запись в базе данных:

>>> b5.name = 'New name'
>>> b5.save()

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

Сохранение полей ForeignKey и ManyToManyField

Обновление ForeignKey работает так же, как и сохранение обычных полей; просто назначьте полю объект необходимого типа. В этом примере обновляется атрибут blog модели Entry объектом entry, предполагается что в базе данных уже существуют используемые объекты:

>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

Обновление ManyToManyField работает немного по-другому; используйте метод add() поля, чтобы добавить связанный объект. В этом примере объект joe модели Author добавляется к объекту entry:

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

Django вызовет исключение, если вы попытаетесь добавить объект неверного типа.

Получение объектов

Для получения объектов из базы данных, создается 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."

Примечание

Обратиться к менеджерам можно только через модель и нельзя через ее экземпляр. Это сделано для разделения “table-level” операций и “record-level” операций.

Manager - главный источник QuerySet для модели. Например, Blog.objects.all() вернет QuerySet, который содержит все объекты Blog из базы данных.

Получение всех объектов

Самый простой способ получить объекты из таблицы - это получить их всех. Для этого используйте метод all() менеджера(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)

Цепочка фильтров

Результат изменения QuerySet - это новый QuerySet и можно использовать цепочки фильтров. Например:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(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 – ленивы, создание 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 вычисляется запросом к базе данных. Для подробностей, в какой момент выполняется запрос, смотрите Когда вычисляется QuerySets.

Получение одного объекта с помощью get

filter() всегда возвращает QuerySet, даже если только один объект возвращен запросом - в этом случае, это будет QuerySet содержащий один объект.

Если вы знаете, что только один объект возвращается запросом, вы можете использовать метод get() менеджера(Manager), который возвращает непосредственно объект:

>>> 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.

Например, этот код возвращает 5 первых объектов (LIMIT 5):

>>> Entry.objects.all()[:5]

Этот возвращает с шестого по десятый (OFFSET 5 LIMIT 5):

>>> Entry.objects.all()[5:10]

Отрицательные индексы (например, Entry.objects.all()[-1]) не поддерживаются.

На самом деле, срез QuerySet возвращает новый QuerySet – запрос не выполняется. Исключением является использовании “шага” в срезе. Например, этот пример выполнил бы запрос, возвращающий каждый второй объект из первых 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.

Фильтры полей выглядят как field__lookuptype=value. (Используется двойное подчеркивание). Например:

>>> 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 базы данных поддерживает около двух дюжин фильтров; полный список можно найти в разделе о фильтрах полей. Вот пример самых используемых фильтров:

exact

“Точное” совпадение. Например:

>>> Entry.objects.get(headline__exact="Cat bites dog")

Создаст такой SQL запрос:

SELECT ... WHERE headline = 'Cat bites dog';

Если вы не указали фильтр – именованный аргумент не содержит двойное подчеркивание – будет использован фильтр exact.

Например, эти два выражения идентичны:

>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied

Это сделано для удобства, т.к. exact самый распространенный фильтр.

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 предлагает удобный и понятный интерфейс для фильтрации по связанным объектам, самостоятельно заботясь о JOIN в SQL. Для фильтра по полю из связанных моделей, используйте имена связывающих полей разделенных двойным нижним подчеркиванием, пока вы не достигните нужного поля.

Этот пример получает все объекты 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 связанного с 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)

Фильтрация по связям многие-ко-многим

Когда вы используете фильтрацию по связанным через ManyToManyField объектам или по обратной связи для ForeignKey, может быть два вида фильтров. Рассмотрим связь Blog/Entry (от Blog к Entry – это связь один-ко-многим). Нам может понадобиться получить блоги с записями, у которых заголовок содержит “Lennon” и которые были опубликованы в 2008. Или нам могут понадобиться блоги с записями с “Lennon” в заголовке и в то же время блоги с записями опубликованными до 2008. Т.к. один Blog может иметь несколько связанных Entry, оба варианта возможны.

Аналогичная ситуация и с ManyToManyField. Например, если Entry имеет ManyToManyField названное tags, нам могут понадобиться записи связанные с тегами “music” и “bands” или нам может понадобиться запись содержащая тег “music” и статусом “public”.

Чтобы обеспечить оба варианта, Django использует определенные правила для вызовов filter(). Все, что в одном вызове filter(), применяется одновременно, чтобы отфильтровать все объекты, соответствующие этим параметрам фильтрации. Успешные вызовы filter() каждый раз сокращают выборку объектов, но для множественных связей, они применяются каждый раз ко всем связанным объектам, а не только к объектам отфильтрованным предыдущим вызовом filter().

Звучит немного непонятно, но пример должен все прояснить. Для выбора всех блогов, содержащих записи и с “Lennon” в заголовке и опубликованные в 2008 (запись должна удовлетворять оба условия), мы будем использовать такой код:

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”. Первый запрос вернет пустой ответ, второй запрос - один блог.

В этом примере, первый фильтр ограничит выборку блогами со связанными записями содержащими “Lennon” в заголовке. Второй фильтр далее ограничит выборку блогами с записями, опубликованными в 2008. Записи выбранные вторым фильтром могут быть такими же, как и из первого фильтра, а могут и не быть. Мы фильтруем объекты Blog с каждым вызовом filter(), а не объекты Entry.

Примечание

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

Например, следующий запрос исключит блоги, с записями, у которых заголовок содержит “Lennon”, а также с записями опубликованными в 2008:

Blog.objects.exclude(
    entry__headline__contains='Lennon',
    entry__pub_date__year=2008,
)

Однако, в отличии от filter(), этот запрос не отфильтрует блоги по записям, которые удовлетворяют двум условиям. Для того, чтобы выбрать все блоги, которые не содержат записи с “Lennon” и опубликованные в 2008, необходимо сделать два запроса:

Blog.objects.exclude(
    entry=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)

Фильтры могут ссылаться на поля модели

В примерах выше мы использовали фильтры, которые сравнивали поля с определенными значениями(константами). Но что, если вы хотите сравнить одно поле с другим полем одной модели?

Django предоставляет класс F для таких сравнений. Экземпляр F() рассматривается как ссылка на другое поле модели. Эти ссылки могут быть использованы для сравнения значений двух разных полей одного объекта модели.

Например, чтобы выбрать все записи, у которых количество комментариев больше, чем “pingback”, мы создаем объект F() с ссылкой на поле “pingback”, и используем этот объект F() в запросе:

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django поддерживает операции суммирования, вычитания, умножения, деления и арифметический модуль для объектов F(), с константами или другими объектами F(). Чтобы найти все записи с количеством комментариев в два раза больше чем “pingbacks”, используем такой запрос:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

Чтобы найти все записи с рейтингом ниже суммы “pingback” и количества комментариев, необходимо выполнить такой запрос:

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

Вы можете использовать два нижних подчеркивания для использования полей связанных объектов в F(). Объект F() с двойным нижним подчеркиванием обеспечит все необходимые JOIN для получения необходимых связанных объектов. Например, чтобы получить все записи, у которых имя автора совпадает с названием блога, нужно выполнить такой запрос:

>>> 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(), например:

>>> F('somefield').bitand(16)

“Shortcut” для фильтрации по первичному ключу

Для удобства, 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 в SQL(iexact, contains, icontains, startswith, istartswith, endswith и iendswith), автоматически экранируют два символа, используемых оператором LIKE – знак процента и нижнего подчеркивания. (В операторе LIKE, знак процента означает “wildcard” из нескольких символов, нижнего подчеркивания - односимвольный “wildcard”.)

Это делает работу с API интуитивно-понятной. Например, чтобы получить все записи со знаком процента, просто используйте символ знака процента как любой другой символ:

>>> Entry.objects.filter(headline__contains='%')

Django самостоятельно позаботится об экранировании; полученный SQL будет выглядеть приблизительно вот так:

SELECT ... WHERE headline LIKE '%\%%';

Также работает и символ нижнего подчеркивания. Оба, знак процента и нижнего подчеркивания, обрабатываются автоматически, прозрачно для вас.

Кэширование и QuerySets

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

В только что созданном QuerySet кеш пустой. После вычисления QuerySet и будет выполнен запрос к базе данных – Django сохраняет результат запроса в кеше QuerySet и возвращает необходимый результат (например, следующий элемент при итерации по QuerySet). Последующие вычисления QuerySet используют кеш.

Помните о кэшировании, чтобы использовать QuerySet правильно. Например, этот код создаст два экземпляра QuerySet и вычислит их не сохраняя:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

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

Чтобы избежать этой проблемы, просто сохраните QuerySet и используйте его повторно:

>>> 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-а, кэш проверяется, но если кэш пустой, выполняется запрос без сохранения его результата в кэш. Это значит, что ограничение выборки, используя индекс или срез, как при использовании списков, не заполнит кэш.

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

>>> queryset = Entry.objects.all()
>>> print queryset[5] # Queries the database
>>> print queryset[5] # Queries the database again

Однако, если уже был загружен весь queryset, он будет использоваться для получения значения:

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[5] # Uses cache
>>> print queryset[5] # Uses cache

Еще несколько примеров, когда загружается весь queryset и результат сохраняется в кэше:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

Примечание

Использование print с queryset не заполнит кэш т.к. будет вызван __repr__(), который показывает только часть объектов.

Сложные запросы с помощью объектов 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, который представляет операцию “OR” двух фильтров с "question__startswith":

Q(question__startswith='Who') | Q(question__startswith='What')

Этот фильтр равнозначен такому оператору SQL WHERE:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

Вы можете комбинировать различные объекты Q с операторами & и | и использовать скобки. Можно использовать оператор ~ для отрицания(NOT) в запросе:

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, двойной знак равно: ==. При этом будут сравнены первичные ключи.

Используя модель 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, {'weblog.Entry': 1})
Изменено в Django 1.9:

Было добавлено возвращение данных об удаленных объектах.

Можно также удалить несколько объектов сразу. Каждый 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 1.9:

Было добавлено возвращение данных об удаленных объектах.

При удалении 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()

Копирование объекта модели

Хоть модель и не содержит встроенного метода для копирования объекта модели, очень просто создать новый экземпляр с копией всех полей другого объекта. Самый простой вариант – установите pk в None. Используя модели из примеров выше:

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

Учитывая, как работает наследование, необходимо установить и pk и id в None:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

Связанные объекты не копируются. Если вы хотите скопировать связанные объекты, нужно написать немного больше кода. В нашем примере, Entry содержит связь многие-ко-многим к модели Author:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors = old_authors # saves new many2many relations

Изменение нескольких объектов

Если вам понадобиться установить значение поля для всех объектов в 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.all().update(blog=b)

Метод update() применяется мгновенно и возвращает количество записей, удовлетворяющих запросу(что может быть не равно количеству обновленных записей, если они уже содержат новые значения). Единственное ограничение для изменяемого QuerySet – он может изменять только одну таблицу в базе данных: главную таблицу модели. Вы можете использовать фильтры по связанным полям, но вы можете изменять поля только таблицы изменяемой модели. Например:

>>> b = Blog.objects.get(pk=1)

# Update all the headlines belonging to this Blog.
>>> Entry.objects.select_related().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 для обновления одного поля значением другого поля модели. Это особенно полезно для изменения счетчика. Например, увеличить значение n_pingbacks на один для каждой записи:

>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

Однако, в отличии от использования объектов F() в методах filter() и exclude(), вы не можете использовать связанные поля при обновлении. Если вы будете использовать связанное поле в объекте F(), буде вызвано исключение FieldError:

# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))

Использование чистого SQL

Если вам нужно создать SQL запрос, который слишком сложен для API Django, вы можете использовать чистый SQL. Django имеет несколько возможностей использовать SQL запросы; смотрите Использование чистого SQL.

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