Предыдущая часть.
Знать как создавать и обновлять данные в базе данных необходимо. Однако, скорее всего ваше веб-приложение будет выполнять больше запросов на получение данных из базы, чем на добавление их. Мы уже встречали пример того, как получить все записи для определённой модели:
In [12]: Publisher.objects.all() Out[12]: [<Publisher: Apress>, <Publisher: O'Reilly>, <Publisher: Apress Publishing>]
В SQL это выглядело примерно так:
SELECT id, name, address, city, state_province, country, website FROM books_publisher;
Примечание
Обратите внимание, что Django не выполняет запрос вида SELECT * во время поиска, а перечисляет все поля явно. Это сделано намеренно — SELECT * иногда может работать медленнее, и (что более важно) — перечисление всех полей более соответствует одному из догматов Python: «Явное лучше, чем неявное«.
Что бы больше узнать о догмах Python — введите import this в командной строке Python.
Давайте рассмотрим подробнее каждую часть из строки Publisher.objects.all():
-
Первым идёт имя модели, которую мы определили —
Publisher. Ничего удивительного — когда мы хотим получить определённые данные, мы используем модель для этих данных. -
Далее идёт атрибут
objects. Он называется менеджер (manager). Менеджеры будет рассмотрены более подробно далее в нашей книге. Пока что — всё, что вам стоит знать, это то что менеджер занимается всеми данными и операциями уровня «таблица».Все модели автоматически получают свой объект-менеджер, и вы будете пользоваться им каждый раз, когда вы занимаетесь объектами моделей.
-
Последним идёт
all(). Это метод менеджераobject, который возвращает все строки в таблице базы. Хотя объект выглядит как список — на самом деле это Набор Запросов (QuerySet) — объект, который представляет специальный набор строк таблицы. В Приложении СQuerySetрассматривается более подробно. В этой главе мы будем их рассматривать как обычные списки.
Каждый поиск в базе будет соответствовать этому шаблону — мы вызываем метод менеджера, который связан с моделью, к которой нам необходимо применить запрос.
Содержание
Фильтрация данных
На самом деле — вам очень редко требуется получить все данные из базы. Как правило — вам требуется какая-то определённая выборка из этих данных. С помощью Django API вы можете отфильтровать данные с помощью метода filter():
In [14]: Publisher.objects.filter(name='Apress') Out[14]: [<Publisher: Apress>]
Аргумент, переданный filter(), транслируется в соответствующий запрос SQL WHERE. Предыдущий пример можно представить в виде SQL примерно так:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress';
Вы можете передавать несколько аргументов, что бы уточнить выборку фильтра:
In [15]: Publisher.objects.filter(country="U.S.A.", state_province="CA") Out[15]: [<Publisher: Apress>, <Publisher: Apress Publishing>]
Несколько аргументов будут сформированы в SQL запрос с помощью AND:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A.' AND state_province = 'CA';
Обратите внимание, что по умолчанию в запросах используется SQL оператор «=». Можно выполнить так:
In [16]: Publisher.objects.filter(name__contains="press") Out[16]: [<Publisher: Apress>, <Publisher: Apress Publishing>]
Двойной символ подчёркивания между name и contains, как в Python, используется Django для обозначения чего-то «магического» — в данном случае, __contains указывает на использование оператор LIKE в SQL-запросе:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%';
Имеется большое количество подобных типов запросов, такие как icontains (регистро-зависимый LIKE), startswith и endswith, range (в SQL — BETWEEN). Все они описываются в Приложении С.
Получение отдельного объекта
В примерах с filter() практически всё, что он возвращает — это набор объектов, которые могут быть обработаны как Python-список. Однако, иногда лучше получить отдельный объект, а не целый список. Для этого в Django имеется метод get():
In [17]: Publisher.objects.get(name="Apress") Out[17]: <Publisher: Apress>
Вместо списка (или QuerySet) — вы получаете отдельный объект. Поэтому вызов, результатом которого будет несколько объектов вызовет ошибку:
In [18]: Publisher.objects.get(country="U.S.A.")
---------------------------------------------------------------------------
MultipleObjectsReturned Traceback (most recent call last)
...
MultipleObjectsReturned: get() returned more than one Publisher -- it returned 3! Lookup parameters were {'country': 'U.S.A.'}
Вызов, который не возвращает ни одного объекта — так же вызове ошибку:
In [19]: Publisher.objects.get(name="Penguin") --------------------------------------------------------------------------- DoesNotExist Traceback (most recent call last) ... DoesNotExist: Publisher matching query does not exist.
Исключение DoesNotExist является атрибутом класса модели — Publisher.DoesNotExist. В вашем приложении вы можете перехватить такие исключения, например так:
In [20]: try: ....: p = Publisher.objects.get(name='Apress') ....: except Publisher.DoesNotExist: ....: print "Apress isn't in the database yet." ....: else: ....: print "Apress is in the database." ....: Apress is in the database.
Сортировка данных
Как вы могли заметить в предыдущих примерах — объекты возвращаются в произвольном порядке. В самом деле — мы пока не указывали в запросах как мы хотим отсортировать результат, мы просто получали в том виде, в каком их выдавала база данных.
В ваших Django приложениях вы скорее всего захотите выполнять какую-то сортировку, например — в алфавитном порядке. Что бы сделать это — используйте метод order():
In [21]: Publisher.objects.order_by("name")
Out[21]: [<Publisher: Apress>, <Publisher: Apress Publishing>, <Publisher: O'Reilly>]
Особой разницы с результатами all() не видно, но теперь наш SQL запрос включается в себя сортировку:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name;
Вы можете выполнять сортировку по любому полю:
In [22]: Publisher.objects.order_by("address")
Out[22]: [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Apress>]
In [23]: Publisher.objects.order_by("state_province")
Out[23]: [<Publisher: Apress>, <Publisher: Apress Publishing>, <Publisher: O'Reilly>]
Для сортировки по нескольким полям, в которой второе поле будет уточнять результат первого, используйте несколько аргументов к order_by():
In [24]: Publisher.objects.order_by("state_province", "address")
Out[24]: [<Publisher: Apress Publishing>, <Publisher: Apress>, <Publisher: O'Reilly>]
Вы можете использовать обратную (reverse) сортировку, добавив символ «-«:
In [25]: Publisher.objects.order_by("-name")
Out[25]: [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Apress>]
In [26]: Publisher.objects.order_by("name")
Out[26]: [<Publisher: Apress>, <Publisher: Apress Publishing>, <Publisher: O'Reilly>]
Хотя такая возможность является очень полезной — использовать её каждый раз может быть неразумным решением. В большинстве случаев вы заранее знаете поля, по которым вы хотели бы выполнять сортировку. В таком случае — вы можете указать сортировку по умолчанию в описании модели Django:
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
Тут вы встречаетесь с новой концепцией — класс Meta, который является вложенным классом в описании класса Publisher. Вы можете использовать класс Meta в любой модели, что бы какие-то специфичные опции для модели. Полное описание опций Meta есть в Приложении B, а в данный момент нас интересует опция ordering. При её использовании — Django будет использовать сортировку по полю name, пока вы явно не укажете другое с помощью опции order_by().
Изменение поиска
Вы уже видели как выполнить фильтрацию данных, и как выполнить их сортировку. Часто, конечно, нужно сделать их вместе. В таком случае — вы можете создать «цепочку» из правил, например:
In [27]: Publisher.objects.filter(country="U.S.A.").order_by("-name")
Out[27]: [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Apress>]
Как вы догадываетесь — в SQL запросе будет применяться и WHERE и ORDER BY:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A' ORDER BY name DESC;
«Срезы» данных
Часто так же необходимо получить только определённое количество строк. Допустим, у вас есть тысяча издателей в базе данных, но вам необходимо получить только первого. Вы можете выполнить это с помощью обычного в Python способа «срезов» по индексам (slicing):
In [28]: Publisher.objects.order_by('name')[0]
Out[28]: <Publisher: Apress>
В виде SQL-запроса это будет выглядеть примерно так:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1;
Также вы можете получить определённый набор данных, используя срез по диапазону индексов:
In [29]: Publisher.objects.order_by('name')[0:2]
Out[29]: [<Publisher: Apress>, <Publisher: Apress Publishing>]
Что в виде SQL будет выглядеть так:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2;
Помните, что отрицательные срезы не поддерживаются:
In [30]: Publisher.objects.order_by('name')[-1]
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
...
AssertionError: Negative indexing is not supported.
Но это можно выполнить с помощью order_by() и обратной сортировкой, например:
In [31]: Publisher.objects.order_by('-name')[0]
Out[31]: <Publisher: O'Reilly>
Обновление нескольких объектов одним запросом
В статье Django Book: добавление и обновление данных мы уже упоминали, что метод модели save() обновляет все колонки в строке. В зависимости от вашего приложения — вы, возможно, захотите обновить только некоторые из этих колонок.
Например, мы хотим обновить издателя Apress, и изменить имя с ‘Apress‘ на ‘Apress Publishing‘. Используя метод save() это выглядело бы так:
>>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save()
А в виде SQL-запроса — примерно так:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;
(в этом примере у издателя Apress его ID — 52)
В этом примере вы можете увидеть, что save() задаёт все значения всех колонок, а не только колонку name. Если в вашей базе данных остальные колонки могут меняться из-за действий других процессов — то было бы логичнее изменить только ту колонку, которая вам действительно необходима. Что бы реализовать это — используйте метод update(). Например:
In [32]: Publisher.objects.filter(id=52).update(name='Apress Publishing') Out[32]: 0
В SQL этот способ будет выглядеть более эффективным и не затронет остальные поля:
UPDATE books_publisher SET name = 'Apress Publishing' WHERE id = 52;
Метод update() применим к любому набору запросов, т.е. — вы можете изменять любые записи в наборе данных. Например, вот как вы можете изменить колонку с ‘U.S.A.‘ на 'USA' для всех издателей:
In [33]: Publisher.objects.all().update(country='USA') Out[33]: 3L
Метод update() возвращает значение — целое число, указывающее сколько записей было изменено. В данном случае — изменение коснулось 3-х записей.
Продолжение — Django Book: удаление объектов