Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Django_-_podrobnoe_rukovodstvo

.pdf
Скачиваний:
306
Добавлен:
01.03.2016
Размер:
4.88 Mб
Скачать

110

Глава 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):

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]