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

Django_-_podrobnoe_rukovodstvo

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

140

Глава 6. Административный интерфейс Django

Настройка форм редактирования

Формы редактирования, как и списки для изменения, можно настраивать.

Для начала изменим порядок следования полей ввода. По умолчанию поля ввода в форме следуют в том порядке, в котором определены в модели. Но это можно изменить с помощью параметра fields в подклассе класса ModelAdmin:

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,)

date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,)

fields = (‘title’, ‘authors’, ‘publisher’, ‘publication_date’)

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

У параметра fields есть еще одна полезная особенность: он позволяет исключить некоторые поля из формы редактирования. Для этого достаточно не перечислять их в списке. Этой возможностью можно воспользоваться, если вы не доверяете администраторам изменять некоторые данные или если какие-то поля изменяются внешней автоматической процедурой. Например, мы могли бы запретить редактирование поля publication_date:

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,)

date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,)

fields = (‘title’, ‘authors’, ‘publisher’)

Теперь форма редактирования не позволит задать дату публикации. Например, это может пригодиться, если вы издатель и не хотите, чтобы авторы могли выражать несогласие с датой публикации (конечно, ситуация чисто гипотетическая).

Когда пользователь отправит такую неполную форму для добавления новой книги, Django просто установит поле publication_date в None, поэтому не забудьте добавить в определение такого поля параметр null=True.

Еще одна часто применяемая настройка формы редактирования имеет отношение к полям типа многие-ко-многим. На примере формы редактирования книг мы видели, что в административном интерфейсе поля типа ManyToManyField представлены в виде списка с множественным выбором. Из элементов ввода данных, имеющихся в HTML, это наиболее подходящий, но работать с такими списками трудновато. Чтобы вы-

Настроечные классы ModelAdmin

141

брать несколько элементов, нужно удерживать клавишу Control (или Command на платформе Mac). Административный интерфейс услужливо выводит соответствующую подсказку, но все равно для списка, содержащего несколько сотен элементов, это неудобно.

Для решения этой проблемы в административном интерфейсе можно использовать параметр filter_horizontal. Добавим его в класс BookAdmin и посмотрим, что произойдет.

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,)

date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) filter_horizontal = (‘authors’,)

(Если вы выполняете предлагаемые упражнения, то обратите внимание, что заодно мы удалили параметр fields, чтобы в форме снова присутствовали все поля.)

Перезагрузив форму редактирования книг, вы увидите, что теперь в разделе Authors (Авторы) появился симпатичный реализованный на JavaScript интерфейс фильтра, позволяющий отыскивать и перемещать интересующих вас авторов из списка Available Authors (Имеющиеся авторы) в список Chosen Authors (Выбранные авторы) или наоборот.

Рис. 6.13. Форма редактирования книги после добавления параметра filter_horizontal

142

Глава 6. Административный интерфейс Django

Мы настоятельно рекомендуем использовать параметр filter_horizontal в случаях, когда количество значений поля типа ManyToManyField больше десяти. Это гораздо проще, чем список с множественным выбором. Кроме того, отметим, что filter_horizontal можно применять и к нескольким полям – достаточно перечислить их имена в кортеже.

Классы ModelAdmin поддерживают также параметр filter_vertical. Он работает так же, как filter_horizontal, только списки расположены не рядом, а один под другим. Каким пользоваться, дело вкуса.

Параметры filter_horizontal и filter_vertical применимы только к полям типа ManyToManyField и не могут использоваться для полей типа ForeignKey. По умолчанию для полей типа ForeignKey в административном интерфейсе используются раскрывающиеся списки <select>, но иногда, как и в случае ManyToManyField, желательно избежать накладных расходов, обусловленных загрузкой сразу всех связанных объектов в список. Например, если в нашей базе данных со временем окажется несколько тысяч издательств, то форма добавления книги Add Book будет загружаться довольно долго, так как названия всех издательств придется скопировать в список <select>.

Дело можно поправить с помощью параметра raw_id_fields. Если присвоить ему в качестве значения кортеж имен полей типа ForeignKey, то такие поля будут представлены в административном интерфейсе простым полем ввода (<input type=”text”>), а не списком <select> (рис. 6.14).

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,)

date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) filter_horizontal = (‘authors’,) raw_id_fields = (‘publisher’,)

Что вводится в это поле? Идентификатор издательства в базе данных. Поскольку люди обычно не помнят эти идентификаторы, то справа от поля находится значок лупы – при щелчке на нем появится всплывающее окно, где можно будет выбрать издательство.

Пользователи, группы и разрешения

Поскольку вы вошли в систему­ как суперпользователь, то имеете право создавать, редактировать и удалять любые объекты. Естественно, в зависимости от обстоятельств могут понадобиться и другие наборы разрешений – нельзя же всем быть суперпользователями. В административном интерфейсе Django реализована система,­ позволяющая разрешать пользователям доступ только к тем частям интерфейса, которые им необходимы.

Пользователи, группы и разрешения

143

 

 

 

 

 

 

Рис. 6.14. Форма редактирования книги после добавления параметра raw_id_fields

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

Вглаве 14 мы расскажем о том, как интегрировать учетные записи

спрочими частями сайта.

Административный интерфейс позволяет редактировать пользователей и разрешения точно так же, как любые другие объекты. Мы уже видели, что в интерфейсе имеются разделы User (Пользователь) и Group (Группа). У пользователя, как и следовало ожидать, есть имя, пароль, адрес электронной почты и реальное имя, а также ряд полей, показывающих, что ему разрешено делать в административном интерфейсе. Во-первых, это три булевских флажка:

•• Флажок «active» показывает, активен ли пользователь. Если он сброшен, то система­ не пустит пользователя даже с правильным паролем.

•• Флажок «staff» управляет тем, разрешено ли пользователю заходить в административный интерфейс (то есть считается ли он «сотрудником» организации). Поскольку эта же система­ может применяться

144

Глава 6. Административный интерфейс Django

для управления доступом к открытым (не административным) частям сайта (см. главу 14), то этот флажок позволяет отличить обычных пользователей от администраторов.

•• Флажок «superuser» предоставляет полный доступ к созданию, изменению и добавлению любых объектов в административном интерфейсе. Если для некоторого пользователя этот флажок отмечен, то все обычные разрешения (или их отсутствие) для него игнорируются.

«Обычным» администраторам, то есть сотрудникам, не являющимся суперпользователями, предоставляется доступ в соответствии с назначенными им разрешениями. Для каждого объекта, который можно редактировать в административном интерфейсе (книги, авторы, издательства), определено три разрешения: создать, редактировать и удалить. Назначение этих разрешений пользователю дает ему соответствующий уровень доступа.

Только что созданный пользователь не имеет никаких разрешений, вы должны назначить их явно. Например, можно разрешить пользователю добавлять и изменять, но не удалять издательства. Отметим, что разрешения определяются на уровне модели, а не объекта, то есть можно сказать, что «Джон может изменять любую книгу», но нельзя сказать, что «Джон может изменять любую книгу, опубликованную издательством Apress». Разрешения на уровне объекта – более сложная тема, которая выходит за рамки данной книги (но рассмотрена в документации по Django).

Примечание

Доступ к редактированию пользователей и разрешений также управляется этой же системой­ разрешений. Если вы разрешите кому-нибудь редактировать пользователей, то он сможет изменить и свои собственные разрешения, а это вряд ли входило в ваши намерения! Разрешение редактировать других пользователей по существу превращает обычного пользователя в суперпользователя.

Пользователей можно также объединять в группы. Группа – это просто набор разрешений, действующих для всех ее членов. Группы полезны, когда нужно назначить одинаковые разрешения сразу нескольким пользователям.

В каких случаях стоит использовать административный интерфейс

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

В каких случаях стоит использовать административный интерфейс

145

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

1.Репортер, отвечающий за проект, встречается с разработчиком и описывает имеющиеся данные.

2.Разработчик проектирует модели Django, описывающие эти данные, а затем открывает административный интерфейс для репортера.

3.Репортер смотрит, все ли необходимые поля присутствуют в интерфейсе и нет ли каких-нибудь лишних; лучше сразу, чем когда будет уже поздно. Разработчик изменяет модель, учитывая замечания. Это итеративная процедура.

4.Когда все модели согласованы, репортер начинает вводить данные

вадминистративном интерфейсе. В это время программист может заняться разработкой представлений и шаблонов­ для открытой части сайта (самая интересная задача!).

Иными словами, смысл административного интерфейса Django в том, чтобы упростить совместную работу поставщиков контента и программистов.

Однако, помимо очевидных задач ввода данных, административный интерфейс может быть полезен еще в нескольких случаях.

•• Проверка моделей данных. После того как модель определена, очень полезно открыть ее в административном интерфейсе и ввести какиенибудь фиктивные данные. Иногда при этом обнаруживаются ошибки проектирования или иные недочеты.

•• Управление собранными данными. В приложениях, где данные поступают из внешних источников (например, от пользователей или веб-роботов), административный интерфейс позволяет просмотреть и отредактировать их. Можно считать, что это менее мощный, зато более удобный аналог командного клиента СУБД.

•• Простенькое приложение для управления данными. Административный интерфейс может стать основой сделанного на скорую руку приложения для управления данными, например, учета собственных расходов. Если вы разрабатываете нечто для своих нужд, а не для широкой публики, то с одним лишь административным интерфейсом можно продвинуться довольно далеко. В некотором смысле его можно рассматривать как специализированный, реляционный вариант электронной таблицы.

Сразу следует прояснить еще один момент: административный интерфейс – не что-то застывшее. На протяжении многих лет мы наблюдали,

146

Глава 6. Административный интерфейс Django

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

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

иправдой.

Что дальше?

Мы создали несколько моделей и настроили первоклассный интерфейс для редактирования данных. В следующей главе мы перейдем к насущному вопросу веб-разработки: созданию и обработке форм.

7

Формы

HTML-формы – становой хребет интерактивных веб-сайтов. Это может быть единственное поле для ввода поискового запроса, как на сайте Google, вездесущая форма для добавления комментария в блог или сложный специализированный интерфейс ввода данных. В этой главе мы расскажем, как Django позволяет обратиться к данным, которые отправил пользователь, проверить их и что-то с ними сделать. Попутно мы расскажем об объектах HttpRequest и Form.

Получение данных из объекта запроса

Мы познакомились с объектами HttpRequest в главе 3 при рассмотрении функций представления, но тогда говорить о них было почти нечего. Напомним, что любая функция представления принимает объект HttpRequest в качестве первого параметра, например:

from django.http import HttpResponse

def hello(request):

return HttpResponse(“Hello world”)

У объекта HttpRequest, каковым является переменная request, есть целый ряд интересных атрибутов и методов, с которыми необходимо познакомиться, чтобы знать, какие существуют возможности. С их помощью можно получить информацию о текущем запросе (например, имя пользователя или версию броузера, который загружает страницу вашего сайта) в момент выполнения функции представления.

Информация об URL

В объекте HttpRequest содержится информация о запрошенном URL, показанная в табл. 7.1.

148

 

 

 

 

Глава 7. Формы

Таблица 7.1. Атрибуты и методы объекта HttpRequest

 

Атрибут/метод

 

Описание

 

Пример

 

 

 

 

 

 

 

 

 

 

request.path

 

Полный путь, не

 

“/hello/”

 

 

 

включая домен, но

 

 

 

 

 

включая ведущий

 

 

 

 

 

символ слеша

 

 

 

request.get_host()

 

Доменное имя

 

“127.0.0.1:8000” или

 

 

 

 

 

“www.example.com”

 

request.get_full_path()

 

Путь path вместе

 

“/hello/?print=true”

 

 

 

со строкой запроса

 

 

 

 

 

(если присутствует)

 

 

 

request.is_secure()

 

True, если запрос

 

True или False

 

 

 

отправлен по

 

 

 

 

 

протоколу HTTPS,

 

 

 

 

 

иначе False

 

 

 

 

 

 

 

 

Всегда пользуйтесь атрибутами и методами, перечисленными в табл. 7.1, а не «зашивайте» URL в код функции представления. В этом случае код будет более гибким, допускающим повторное использование в других местах. Вот простенький пример:

# ТАК ПЛОХО!

def current_url_view_bad(request):

return HttpResponse(“Добро пожаловать на страницу /current/”)

# А ТАК ХОРОШО

def current_url_view_good(request):

return HttpResponse(“Добро пожаловать на страницу %s” % request.path)

Другая информация о запросе

request.META – это словарь Python, содержащий все HTTP-заголовки данного запроса, включая IP-адрес пользователя и информацию об агенте пользователя (обычно название и номер версии веб-броузера). Отметим, что в список входят как заголовки, отправленные пользователем, так и те, что были установлены вашим веб-сервером. Ниже перечислены некоторые часто встречающиеся ключи словаря:

•• HTTP_REFERER: ссылающийся URL, если указан. (Обратите внимание на ошибку в написании слова REFERER.)

•• HTTP_USER_AGENT: строка с описанием агента пользователя (если указана). Выглядит примерно так:

“Mozilla 5.0 (X11; U; Linux i686) Gecko/20080829 Firefox/2.0.0.17”

•• REMOTE_ADDR: IP-адрес клиента, например “12.345.67.89”. (Если запрос проходил через прокси-серверы, то это может быть список IP-адресов, разделенных запятыми, например “12.345.67.89,23.456.78.90”.)

Получение данных из объекта запроса

149

Отметим, что поскольку request.META – обычный словарь Python, то при попытке обратиться к несуществующему ключу будет возбуждено исключение KeyError. (Поскольку HTTP-заголовки – это внешние данные, то есть отправлены пользовательским броузером, доверять им нельзя, поэтому вы должны проектировать свое приложение так, чтобы оно корректно обрабатывало ситуацию, когда некоторый заголовок пуст или отсутствует.) Нужно либо использовать try/except-блок, либо осуществлять доступ по ключу методом get():

# ПЛОХО!

def ua_display_bad(request):

ua = request.META[‘HTTP_USER_AGENT’] # Может возникнуть KeyError! return HttpResponse(“Ваш броузер %s” % ua)

# ХОРОШО (ВАРИАНТ 1)

def ua_display_good1(request): try:

ua = request.META[‘HTTP_USER_AGENT’] except KeyError:

ua = ‘unknown’

return HttpResponse(“Ваш броузер %s” % ua)

# ХОРОШО (ВАРИАНТ 2)

def ua_display_good2(request):

ua = request.META.get(‘HTTP_USER_AGENT’, ‘unknown’) return HttpResponse(“Ваш броузер %s” % ua)

Мы предлагаем написать несложное представление, которое будет отображать все содержимое словаря request.META, чтобы вы знали, что в нем находится. Вот один из возможных вариантов такого представления:

def display_meta(request): values = request.META.items() values.sort()

html = []

for k, v in values: html.append(‘<tr><td>%s</td><td>%s</td></tr>’ % (k, v))

return HttpResponse(‘<table>%s</table>’ % ‘\n’.join(html))

В качестве упражнения попробуйте преобразовать это представление

вшаблон­ Django, вынеся HTML-разметку из кода. Кроме того, попробуйте включить в состав выводимой информации значение request.path и других атрибутов HttpRequest, которые были описаны в предыдущем разделе.

Информация об отправленных данных

Помимо основных метаданных о запросе объект HttpRequest имеет два атрибута, в которых хранится отправленная пользователем информация: request.GET и request.POST. Тот и другой объекты похожи на словарь

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