Django_-_podrobnoe_rukovodstvo
.pdf110 |
Глава 5. Модели |
Это легко исправить, добавив в класс Publisher метод __unicode__(). Этот метод определяет внешнее представление объекта в виде Unicodeстроки. Чтобы увидеть его в действии, добавьте в три наши модели следующие определения метода __unicode__():
from django.db import models
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 Author(models.Model):
first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField()
def __unicode__(self):
return u’%s %s’ % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
def __unicode__(self): return self.title
Как видите, метод __unicode__() может выполнять произвольные действия, необходимые, чтобы получить строковое представление объекта. В данном случае для объектов Publisher и Book мы возвращаем название издательства или книги соответственно, а для объекта Author метод
__unicode__() чуть сложнее, он объединяет поля first_name и last_name, разделяя их пробелом. Единственное требование к методу __unicode__() состоит в том, что он должен возвращать объект класса Unicode. Если он вернет какой-то другой тип, например, целое число, то Python возбудит исключение TypeError с сообщением «coercing to Unicode: need string or buffer, int found» (приведение к типу Unicode: ожидается строка или буфер, получено int).
Чтобы изменения, связанные с добавлением методов __unicode__(), вступили в силу, выйдите из оболочки Python и снова войдите в нее, выполнив команду python manage.py shell. (Это самый простой способ актуализировать изменения.) Теперь список объектов Publisher выглядит гораздо понятнее:
Добавление строковых представлений моделей |
111 |
Объекты Unicode
Что такое объекты Unicode?
Можете считать, что это строка Python, в которой могут встречаться более миллиона разных символов: латиница с диакритическими знаками, нелатинские символы, фигурные кавычки и совсем уж странные знаки.
Обычные строки Python кодированы, то есть представлены
вкакой-то конкретной кодировке, например, ASCII, ISO-8859-1 или UTF-8. Сохраняя нестандартные символы (то есть все, кроме 128, находящихся в первой половине таблицы ASCII, куда входят, в частности, цифры и латинские буквы), вы должны помнить,
вкакой кодировке представлена строка, иначе эти символы будут выглядеть странно при отображении и печати. Проблемы начинаются при попытке объединить данные, сохраненные в одной кодировке, с данными в другой кодировке, а также при попытке отобразить их в приложении, рассчитанном на определенную кодировку. Все мы видели веб-страницы и электронные письма, испещренные вопросительными знаками «??? ??????» или другими символами. Это признак проблем с кодировкой.
У объектов же Unicode кодировки нет, они представлены с помощью универсального набора символов, называемого Unicode. Объекты Unicode в Python можно объединять как угодно, не опасаясь, что возникнут сложности с кодировкой.
В Django объекты Unicode используются повсеместно. Объекты модели, выбранные из базы, представлены как объекты Unicode, представления работают с данными Unicode, при отображении шаблонов также применяется Unicode. Вам обычно не приходится думать о правильности выбранной кодировки, все работает «само».
Отметим, что это весьма поверхностный обзор объектов Unicode, и вы должны пообещать себе, что изучите эту тему внимательнее. Начать можно со страницы http://www.joelonsoftware.com/ articles/Unicode.html
>>>from books.models import Publisher
>>>publisher_list = Publisher.objects.all()
>>>publisher_list
[<Publisher: Apress>, <Publisher: O’Reilly>]
Обязательно определяйте метод __unicode__() во всех своих моделях – не только ради собственного удобства при работе с интерактивным интерпретатором, но и потому, что сам фреймворк Django в нескольких местах вызывает этот метод для отображения объектов.
112 |
Глава 5. Модели |
Наконец, отметим, что метод __unicode__() – прекрасный пример добавления поведения в модель. Модель Django описывает не только структуру таблицы базы данных, но и все действия, которые умеет выполнять объект. Метод __unicode__() – один из примеров таких действий: объект модели знает, как отобразить себя.
Вставка и обновление данных
Вы уже видели, как это делается. Чтобы вставить строку в таблицу, сначала создайте экземпляр модели, пользуясь именованными аргументами, например:
>>> p = Publisher(name=’Apress’,
... |
address=’2855 Telegraph Ave.’, |
... |
city=’Berkeley’, |
... |
state_province=’CA’, |
... |
country=’U.S.A.’, |
... |
website=’http://www.apress.com/’) |
Сам факт создания экземпляра модели не вызывает обращения к базе данных. Запись не сохраняется в базе, пока не будет вызван метод save():
>>> p.save()
На язык SQL это транслируется примерно так:
INSERT INTO books_publisher
(name, address, city, state_province, country, website) VALUES
(‘Apress’, ‘2855 Telegraph Ave.’, ‘Berkeley’, ‘CA’, ‘U.S.A.’, ‘http://www.apress.com/’);
Поскольку в модели Publisher имеется автоинкрементный первичный ключ id, то при первом вызове save() производится еще одно действие: для данной записи вычисляется значение первичного ключа, которое записывается в атрибут id экземпляра:
>>> p.id
52 # для ваших данных значение может быть другим
При последующих обращениях к save() запись обновляется на месте без создания новой (то есть выполняется SQL-команда UPDATE, а не INSERT):
>>>p.name = ‘Apress Publishing’
>>>p.save()
Этот вызов save() транслируется примерно в такую SQL-команду:
UPDATE books_publisher SET name = ‘Apress Publishing’,
address = ‘2855 Telegraph Ave.’, city = ‘Berkeley’, state_province = ‘CA’,
country = ‘U.S.A.’,
Выборка объектов |
113 |
website = ‘http://www.apress.com’ WHERE id = 52;
Обратите внимание, что обновляются все поля, а не только те, что изменились. В зависимости от особенностей приложения это может привести к конкуренции. О том, как выполнить следующий (несколько отличающийся) запрос, см. раздел «Обновление нескольких объектов одной командой» ниже:
UPDATE books_publisher SET name = ‘Apress Publishing’
WHERE id=52;
Выборка объектов
Знать, как создаются и обновляются записи базы данных, важно, но, скорее всего, ваши веб-приложения будут заниматься главным образом выборкой существующих объектов, а не созданием новых. Вы уже видели, как можно выбрать все записи для данной модели:
>>> Publisher.objects.all()
[<Publisher: Apress>, <Publisher: O’Reilly>]
Этот вызов транслируется в такую SQL-команду:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher;
Примечание
При выборке данных Django не употребляет команду SELECT *, а всегда перечисляет все поля явно. Так сделано специально: при определенных условиях SELECT * может выполняться медленнее, и (что важнее) явное перечисление полей в большей степени отвечает одному из основополагающих принципов Python: «Явное всегда лучше неявного». С другими постулатами Python можно познакомиться, набрав команду import this в ответ на приглашение интерпретатора.1
Рассмотрим отдельные части выражения Publisher.objects.all() более пристально.
•• Во-первых, мы имеем саму модель Publisher. Тут нет ничего удивительного: если хотите получить данные, нужна модель этих данных.
•• Далее следует атрибут objects, который называется менеджером. Подробно менеджеры обсуждаются в главе 10. А пока достаточно знать, что менеджеры отвечают за операции «уровня таблицы»,
в том числе за самую важную из них – выборку данных.
1 Те же постулаты философии языка Python на русском языке можно найти на странице http://ru.wikipedia.org/wiki/Python. – Прим. науч. ред.
114 |
Глава 5. Модели |
•• У любой модели автоматически имеется менеджер objects; им в любой момент можно воспользоваться для выборки данных.
•• И наконец, метод all(). Это метод менеджера objects, который возвращает все строки из таблицы базы данных. Хотя выглядит этот объект как список, на самом деле он является экземпляром класса QuerySet и представляет набор строк таблицы. В приложении C класс QuerySet рассматривается более подробно. А в этой главе мы будем обращаться с объектами этого класса как со списками, которые они, собственно, и имитируют.
Любая операция выборки из базы данных устроена таким же образом – вызываются методы менеджера, ассоциированного с опрашиваемой моделью.
Фильтрация данных
Естественно, выбирать из таблицы все записи приходится редко; как правило, нас интересует какое-то подмножество данных. В Django API для фильтрации данных применяется метод filter():
>>> Publisher.objects.filter(name=’Apress’) [<Publisher: Apress>]
Метод filter() принимает именованные аргументы, которые транслируются в соответствующее предложение WHERE SQL-команды SELECT, например:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = ‘Apress’;
Чтобы еще больше сузить область поиска, можно передать несколько аргументов:
>>> Publisher.objects.filter(country=”U.S.A.”, state_province=”CA”) [<Publisher: Apress>]
При наличии нескольких аргументов они объединяются в предложении WHERE с помощью оператора AND. Таким образом, предыдущий пример транслируется в такую команду:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = ‘U.S.A.’
AND state_province = ‘CA’;
Обратите внимание, что по умолчанию при поиске используется SQLоператор =, проверяющий точное совпадение с искомым текстом. Возможны и другие виды сравнения:
>>> Publisher.objects.filter(name__contains=”press”) [<Publisher: Apress>]
Выборка объектов |
115 |
Между словами name и contains должно быть два знака подчеркивания. В Django, как и в самом языке Python, два символа подчеркивания говорят о том, что происходит нечто «магическое» – в данном случае часть __contains транслируется в SQL-оператор LIKE:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE ‘%press%’;
Существует много других видов поиска, в том числе icontains (LIKE без учета регистра), startswith (начинается), endswith (заканчивается) и range (транслируется в оператор SQL BETWEEN). Все эти варианты поиска подробно описаны в приложении C.
Выборка одиночного объекта
Во всех предыдущих примерах метода filter() возвращались объекты QuerySet, с которыми можно работать как со списками. Но иногда удобнее выбрать всего один объект. Для этого предназначен метод get():
>>> Publisher.objects.get(name=”Apress”) <Publisher: Apress>
Теперь вместо списка объектов (точнее, вместо объекта QuerySet) возвращается единственный объект. Поэтому, если запрос в действительности возвращает несколько объектов, то возбуждается исключение:
>>> Publisher.objects.get(country=”U.S.A.”) Traceback (most recent call last):
...
MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {‘country’: ‘U.S.A.’}
Запрос, не возвращающий ни одного объекта, также приводит к исключению:
>>> Publisher.objects.get(name=”Penguin”) Traceback (most recent call last):
...
DoesNotExist: Publisher matching query does not exist.
Исключение DoesNotExist является атрибутом класса модели: Publisher. DoesNotExist. В приложении эти исключения следует перехватывать:
try:
p = Publisher.objects.get(name=’Apress’) except Publisher.DoesNotExist:
print “Apress еще нет в базе данных.” else:
print “Apress есть в базе данных.”
116 |
Глава 5. Модели |
Сортировка данных
Выполняя предыдущие примеры, вы могли заметить, что объекты возвращаются в случайном порядке. Да, зрение вас не обманывает; раз мы не сказали, как упорядочивать результаты, база данных возвращает их
впорядке, который для нас выглядит случайным.
Всвоем приложении Django вы, наверное, захотите отсортировать результат по какому-нибудь значению, например, по названию в алфавитном порядке. Для этого предназначен метод order_by():
>>> Publisher.objects.order_by(“name”) [<Publisher: Apress>, <Publisher: O’Reilly>]
Отличие от метода all() вроде бы невелико, но теперь в SQL-команде указывается способ сортировки:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name;
Сортировать можно по любому полю:
>>>Publisher.objects.order_by(“address”) [<Publisher: O’Reilly>, <Publisher: Apress>]
>>>Publisher.objects.order_by(“state_province”) [<Publisher: Apress>, <Publisher: O’Reilly>]
Чтобы отсортировать по нескольким полям (второе поле устраняет неоднозначность в случае, когда первое в нескольких записях одинаково), нужно задать несколько аргументов:
>>> Publisher.objects.order_by(“state_province”, “address”) [<Publisher: Apress>, <Publisher: O’Reilly>]
Можно также изменить порядок сортировки на противоположный, поставив перед именем поля знак ‘-’:
>>> Publisher.objects.order_by(“-name”) [<Publisher: O’Reilly>, <Publisher: Apress>]
Хотя такая гибкость полезна, постоянное использование order_by() может надоесть. Как правило, сортировка производится по какому-то одному полю. Для таких случаев 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()
Выборка объектов |
117 |
def __unicode__(self): return self.name
class Meta:
ordering = [‘name’]
Здесь мы вводим новую концепцию: class Meta. Это класс, вложенный
вопределение класса Publisher (чтобы показать, что это часть класса Publisher, он вводится с отступом). Класс Meta можно использовать
влюбой модели для определения различных специальных параметров. Полный перечень параметров Meta приведен в приложении B, а пока нас будет интересовать только параметр ordering. Он сообщает платформе Django, что если порядок сортировки не задан явно с помощью вызова метода order_by(), то объекты Publisher, извлекаемые с помощью API доступа к данным, должны упорядочиваться по полю name.
Последовательная выборка
Мы показали, как фильтровать и сортировать данные. Но часто нужно сделать то и другое одновременно. В таких случаях методы выборки достаточно просто объединить в цепочку:
>>> Publisher.objects.filter(country=”U.S.A.”).order_by(“-name”) [<Publisher: O’Reilly>, <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 для извлечения среза из списка:
>>> Publisher.objects.order_by(‘name’)[0] <Publisher: Apress>
Это транслируется в такую SQL-команду:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
LIMIT 1;
Аналогично для выборки некоторого подмножества данных можно воспользоваться синтаксисом для получения фрагмента списка:
118 |
Глава 5. Модели |
>>>Publisher.objects.order_by(‘name’)[0:2]
Врезультате возвращаются два объекта, что эквивалентно такой команде:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;
Отметим, что отрицательные индексы не поддерживаются:
>>> Publisher.objects.order_by(‘name’)[-1] Traceback (most recent call last):
...
AssertionError: Negative indexing is not supported.
Но это ограничение легко обойти, изменив порядок сортировки с помощью метода order_by():
>>> Publisher.objects.order_by(‘-name’)[0]
Обновление нескольких объектов одной командой
В разделе «Вставка и обновление данных» мы отметили, что метод модели save() обновляет все столбцы строки. Но иногда требуется обновить только часть столбцов.
Предположим, например, что нужно обновить объект Publisher, изменив название с ‘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;
Примечание
Здесь предполагается, что идентификатор (id) издательства Apress равен 52.
Удаление объектов |
119 |
Как видно из этого примера, метод save() обновляет значения всех столбцов, а не только столбца name. Но если другие столбцы могут быть одновременно изменены каким-то другим процессом, то было бы разумнее обновлять только тот столбец, который действительно изменился. Для этого воспользуемся методом update() объекта QuerySet, например:
>>> Publisher.objects.filter(id=52).update(name=’Apress Publishing’)
Такой вызов транслируется в гораздо более эффективную SQL-команду, устраняя возможность конкуренции:
UPDATE books_publisher
SET name = ‘Apress Publishing’
WHERE id = 52;
Метод update() может вызываться для любого объекта QuerySet, что позволяет обновлять несколько записей одной командой. Вот, например, как можно изменить поле country с ‘U.S.A.’ на USA во всех записях
Publisher:
>>> Publisher.objects.all().update(country=’USA’) 2
Метод update() возвращает целочисленное значение, равное количеству обновленных записей. В примере выше оно равно 2.
Удаление объектов
Для удаления объекта из базы данных достаточно вызвать его метод delete():
>>>p = Publisher.objects.get(name=”O’Reilly”)
>>>p.delete()
>>>Publisher.objects.all()
[<Publisher: Apress Publishing>]
Можно удалить сразу несколько объектов, вызвав метод delete() для объекта QuerySet аналогично тому, как мы вызывали метод update()
впредыдущем разделе:
>>>Publisher.objects.filter(country=’USA’).delete()
>>>Publisher.objects.all().delete()
>>>Publisher.objects.all()
[]
Но будьте осторожны при удалении данных! В качестве меры предосторожности Django требует явно использовать метод all(), если вы хотите удалить все записи из таблицы.
Например, такой вызов приведет к появлению ошибки:
>>> Publisher.objects.delete() Traceback (most recent call last):