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

Django_-_podrobnoe_rukovodstvo

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

90

Глава 4. Шаблоны­

<h1>Мой сайт точного времени</h1> <p>Сейчас {{ current_date }}.</p>

<hr>

<p>Спасибо, что посетили мой сайт.</p> </body>

</html>

Вроде бы неплохо, но что если мы захотим создать шаблон­ еще для одного представления, например, hours_ahead из главы 3? Чтобы получить корректный и полный HTML-шаблон,­ нам пришлось бы написать что-то в этом роде:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”ru”>

<head>

<title>Время в будущем</title> </head>

<body>

<h1>Мой сайт точного времени</h1>

<p>Через {{ hour_offset }} часов будет {{ next_time }}.</p>

<hr>

<p>Спасибо, что посетили мой сайт.</p> </body>

</html>

Видно, что значительная часть HTML-разметки просто продублирована. В случае типичного сайта с панелью навигации, несколькими таблицами стилей, быть может, JavaScript-сценариями каждый шаблон­ содержал бы разнообразный повторяющийся код.

Если решать эту проблему путем включения на стороне сервера, то следовало бы вынести общие фрагменты из обоих шаблонов,­ поместить их в отдельные фрагментарные шаблоны­ и затем включать их в каждый шаблон­. Наверное, вы сохранили бы начало шаблона­ в файле header. html:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”ru”>

<head>

А конец, скорее всего, поместили бы в файл footer.html:

<hr>

<p>Спасибо, что посетили мой сайт.</p> </body>

</html>

Стратегия включения хорошо подходит для верха и низа страницы. А вот с серединой придется повозиться. В данном примере обе страницы имеют заголовок – <h1>Мой сайт точного времени</h1>, но включить его в файл header.html нельзя, потому что тег <title> на этих страницах различается. Если бы мы перенесли <h1> в header.html, то пришлось бы пере-

Наследование шаблонов­

91

носить и <title>, но тогда мы не смогли бы сделать разные заголовки на разных страницах. Понимаете, к чему все идет?

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

Первым делом нужно определить базовый шаблон­ – скелет страницы, в который позже будут вставлены дочерние шаблоны­. Вот как выглядит базовый шаблон­ в нашем примере:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”ru”>

<head>

<title>{% block title %}{% endblock %}</title> </head>

<body>

<h1>Мой сайт точного времени</h1> {% block content %}{% endblock %} {% block footer %}

<hr>

<p>Спасибо, что посетили мой сайт.</p> {% endblock %}

</body>

</html>

В этом шаблоне,­ который мы назовем base.html, определен простой HTML-документ, описывающий структуру всех страниц сайта. Дочерние шаблоны­ могут переопределить некоторые блоки, дополнить их или оставить без изменения. (Если вы выполняете приведенные в тексте упражнения, сохраните этот файл в каталоге шаблонов­ под именем base.html.)

Здесь мы воспользовались не встречавшимся ранее тегом {% block %}. Он просто сообщает системе­ о том, что некоторая часть шаблона­ может быть переопределена в дочернем шаблоне­.

Имея базовый шаблон,­ мы можем соответствующим образом модифицировать шаблон­ current_datetime.html:

{% extends “base.html” %}

{% block title %}Текущее время{% endblock %}

{% block content %}

<p>Сейчас {{ current_date }}.</p> {% endblock %}

Заодно давайте уж создадим шаблон­ для представления hours_ahead из главы 3. (Оставляем в качестве упражнения модификацию hours_ahead так, чтобы вместо «зашитой» HTML-разметки в нем использовались шаблоны­.) Вот как он выглядит:

92 Глава 4. Шаблоны­

{% extends “base.html” %}

{% block title %}Время в будущем{% endblock %}

{% block content %}

<p>Через {{ hour_offset }} часов будет {{ next_time }}.</p> {% endblock %}

Ну не прелесть ли? Каждый шаблон­ содержит только данные, уникальные для него самого. Никакого дублирования. Чтобы изменить общий дизайн сайта, достаточно модифицировать только base.html, и изменения немедленно отразятся на всех страницах.

Объясним,какэтоработает.Вовремязагрузкишаблона­current_datetime. html система­ видит тег {% extends %}, означающий, что это дочерний шаб­ лон. Поэтому система­ тут же загружает родительский шаблон­ – в данном случае base.html.

Теперь система­ обнаруживает три тега {% block %} в файле base.html и заменяет их содержимым дочернего шаблона­. Таким образом, будет использован заголовок, определенный в {% block title %}, и содержимое, определенное в {% block content %}.

Отметим, что в дочернем шаблоне­ не определен блок footer, поэтому сис­ тема шаблонов­ берет значение из родительского шаблона­. Содержимое тега {% block %} в родительском шаблоне­ используется, когда нет никакого другого варианта.

Наследование никак не сказывается на контексте шаблона­. Иными словами, любой шаблон­ в дереве наследования имеет доступ ко всем шаблонным­ переменным, заданным в контексте.

Количество уровней наследования не ограничено. Часто применяется следующая трехуровневая схема наследования:

1.Создать шаблон­ base.html, который определяет общий облик сайта.

Внего входят редко изменяющиеся части.

2.Создать по одному шаблону­ base_SECTION.html для каждого раздела сайта (например, base_photos.html и base_forum.html). Эти шаблоны­ наследуют base.html и определяют стили и дизайн, характерные для каждого раздела.

3.Создать отдельные шаблоны­ для каждого типа страницы, например, страницы форума или фотогалереи. Они наследуют шаблоны­ соответствующего раздела.

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

Приведем несколько рекомендаций по работе с механизмом наследо­­ вания шаблонов­.

Что дальше?

93

•• Если в шаблоне­ встречается тег {% extends %}, то он должен быть самым первым тегом. В противном случае механизм наследования работать не будет.

•• Вообще говоря, чем больше тегов {% block %} в базовых шаблонах,­ тем лучше. Напомним, что дочерние шаблоны­ не обязаны переопределять все блоки родительского шаблона,­ поэтому во многих блоках можно определить разумные значения по умолчанию и переопределять только те, что необходимы. Лучше, когда точек встраивания в избытке, а не в недостатке.

•• При обнаружении в нескольких шаблонах­ повторяющихся фрагментов кода имеет смысл перенести этот код в тег {% block %} в родительском шаблоне­.

•• Чтобы получить содержимое блока в родительском шаблоне,­ используйте конструкцию {{ block.super }}. Эта «магическая» переменная содержит результат отображения текста из родительского шаблона­. Это бывает полезно, когда требуется дополнить содержимое родительского блока, а не переопределить его полностью.

•• Нельзя определять в одном шаблоне­ несколько тегов {% block %} с одним и тем же именем. Это ограничение связано с тем, что тег block работает в обоих направлениях. Иначе говоря, блок – не просто дыра, которую нужно заполнить, а еще и содержимое, которым эта дыра заполняется в родительском шаблоне­. Если бы в одном шаб­ лоне встретились несколько одноименных тегов {% block %}, родитель этого шаблона­ не знал бы, содержимое какого блока использовать.

•• Шаблон,­ имя которого задано в теге {% extends %}, загружается так же, как это делает функция get_template(), то есть имя шаблона­ конкатенируется с путем, заданным параметром TEMPLATE_DIRS.

•• Обычно аргументом {% extends %} является строка, но может быть и переменная, если имя родительского шаблона­ становится известно только на этапе выполнения. Это открывает возможность для различных хитроумных динамических трюков.

Что дальше?

Итак, ваш багаж пополнился знаниями о системе­ шаблонов­ в Django. Что дальше?

Во многих современных сайтах используются базы данных, то есть содержимое сайта хранится в реляционной базе. Это позволяет четко отделить данные от логики их обработки (точно так же, как представления и шаблоны­ разделяют логику и отображение).

В следующей главе мы рассмотрим средства, предоставляемые Django для взаимодействия с базами данных.

5

Модели

Вглаве 3 мы рассмотрели основы построения динамических веб-сайтов

спомощью Django: создание представлений и настройку шаблонов­ URL. Мы сказали, что представление может реализовывать произвольную логику и должно вернуть ответ. В качестве одного из примеров такой произвольной логики мы привели вычисление текущих даты и времени.

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

Многие развитые сайты сочетают обе возможности. Например, Amazon. com – прекрасный пример сайта, управляемого данными. Страница каждого продукта представляет собой результат запроса к базе данных Amazon, отформатированный в виде HTML. А отправленный вами отзыв сохраняется в базе данных.

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

Примечание

Для использования уровня доступа к базе данных в Django знакомство с теорией реляционных баз данных и языком SQL, строго говоря, необязательно, однако настоятельно рекомендуется. Введение в тематику баз данных выходит за рамки настоящей книги, но не бросайте чтение, даже если вы новичок в этой области. Вы сумеете ухватить суть, опираясь на контекст.

Прямолинейный способ обращения к базе данных из представления

95

Прямолинейный способ обращения

кбазе данных из представления

Вглаве 3 был описан непосредственный способ генерации выходной информации в представлении (путем включения текста прямо в код функции). Точно так же существует непосредственный способ обращения

кбазе данных из представления. Достаточно воспользоваться любой из существующих библиотек на Python, которые позволяют выполнить SQL-запрос и что-то сделать с полученным результатом.

Вследующем примере мы воспользовались библиотекой MySQLdb (ее можно загрузить со страницы http://www.djangoproject.com/r/pythonmysql/), чтобы подключиться к СУБД MySQL, выбрать некоторые записи и передать их в шаблон­ для отображения на веб-странице:

from django.shortcuts import render_to_response import MySQLdb

def book_list(request):

db = MySQLdb.connect(user=’me’, db=’mydb’, passwd=’secret’, host=’localhost’)

cursor = db.cursor()

cursor.execute(‘SELECT name FROM books ORDER BY name’) names = [row[0] for row in cursor.fetchall()] db.close()

return render_to_response(‘book_list.html’, {‘names’: names})

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

•• Мы «зашили» в код параметры соединения с базой данных. В идеале они должны храниться в файле с настройками Django.

•• Приходится писать много шаблонного­ кода: создать соединение, создать курсор, выполнить команду и закрыть соединение. Хотелось бы, чтобы можно было просто сказать, какие данные нужны.

•• Мы жестко связали себя с MySQL. Если позже мы захотим перейти с MySQL на PostgreSQL, то придется взять другой адаптер базы данных (psycopg вместо MySQLdb), изменить параметры соединения и – в зависимости от характера SQL-команды – быть может, переписать запрос. В идеале СУБД должна быть абстрагирована, так чтобы при смене СУБД было достаточно внести изменение в одно место. (Это особенно желательно, когда вы пишете на Django приложение с открытым исходным кодом, которое должно быть полезно как можно большему количеству людей.)

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

96

Глава 5. Модели

from django.shortcuts import render_to_response from mysite.books.models import Book

def book_list(request):

books = Book.objects.order_by(‘name’)

return render_to_response(‘book_list.html’, {‘books’: books})

Далее мы подробно объясним, как действует этот код. А пока просто полюбуйтесь на него.

Шаблон­ проектирования MTV (или MVC)

Прежде чем продолжить изучение кода, поговорим немного об общем подходе к проектированию веб-приложений, работающих с данными,

вDjango.

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

Так, говоря о функциях представлений, мы обсудили, как важно отделять бизнес-логику от логики отображения с помощью системы­ шаб­ лонов. Уровень работы с базой данных – применение той же идеологии

клогике доступа к данным.

Всовокупности эти три части – логика доступа к данным, бизнес-логика и логика отображения – составляют шаблон­ проектирования, который иногда называют Модель-Представление-Контроллер (Model-View-Con­ troller – MVC). Здесь «модель» относится к уровню доступа к данным,

«представление» – к той части системы,­ которая выбирает, что отображать и как отображать, а «контроллер» – к части системы,­ которая в зависимости от введенных пользователем данных решает, какое представление использовать, и при необходимости обращается к модели.

Зачем вводить акроним?

Шаблоны­ проектирования, такие как MVC, определяются, преж­ де всего, чтобы облегчить общение разработчиков между собой. Вместо того чтобы говорить коллеге: «Давай введем абстракцию доступа к данным, потом организуем отдельный уровень обработки вывода данных, а между ними вставим посредника», вы можете обратиться к общепринятому лексикону и сказать: «А давайка применим здесь шаблон­ проектирования MVC».

Django следует шаблону­ MVC в такой мере, что его можно было бы назвать MVC-фреймворком.

Настройка базы данных

97

Опишем примерное соответствие между буквами M, V, C и концепциями Django:

•• M – часть, касающаяся доступа к данным; соответствует уровню работы с базой данных в Django; он описывается в этой главе;

•• V – часть, касающаяся решения о том, что и как отображать, соответствует представлениям и шаблонам;­

•• C – часть, которая передает управление некоторому представлению в зависимости от того, что ввел пользователь, реализована самим фреймворком с помощью конфигурации URL, которая говорит, какую функцию представления вызывать для данного URL.

Поскольку буква «C» реализована самим фреймворком, а самое ин­ тересное происходит в моделях, шаблонах­ и представлениях, то по­ этому Django стали называть MTV-фреймворком, где:

•• M означает «Model» (модель), то есть уровень доступа к данным. На этом уровне сосредоточена вся информация о данных: как получить к ним доступ, как осуществлять контроль, каково их поведение, каковы отношения между данными.

•• T означает «Template» (шаблон),­ уровень отображения. Здесь принимаются решения, относящиеся к представлению данных: как следует отображать данные на веб-странице или в ином документе.

•• V означает «View» (представление), уровень бизнес-логики. На этом уровне расположена логика доступа к модели и выбора подходящего шаблона­ (или шаблонов)­ . Можно сказать, что это мост между моделями и шаблонами­.

Если вы знакомы с другими MVC-фреймворками веб-разработки, например Ruby on Rails, то можете считать, что представления Django – это «контроллеры», а шаблоны­ Django – «представления». Это досадное различие в словоупотреблении вызвано различными интерпретациями шаблона­ проектирования MVC. В интерпретации Django представление описывает данные, показываемые пользователю; речь идет не столько о том, как выглядят данные, сколько о том, какие данные показываются. Напротив, в Ruby on Rails и других подобных фреймворках предполагается, что принятие решения о том, какие данные показывать, входит в задачу контроллера, тогда как представление определяет, как данные выглядят, а не какие данные показываются.

Ни одна из интерпретаций не является более «правильной». Важно лишь понимать, какие идеи лежат в основе.

Настройка базы данных

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

98

Глава 5. Модели

ся о задании начальной конфигурации; мы должны сообщить Django, с какой СУБД собираемся работать и как к ней подключиться.

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

Как и параметр TEMPLATE_DIRS в предыдущей главе, конфигурация базы данных задается в файле параметров Django, который по умолчанию называется settings.py. Откройте этот файл и найдите в нем следующие параметры:

DATABASE_ENGINE = ‘’

DATABASE_NAME = ‘’

DATABASE_USER = ‘’

DATABASE_PASSWORD = ‘’

DATABASE_HOST = ‘’

DATABASE_PORT = ‘’

Ниже приведены описания каждого параметра.

•• Параметр DATABASE_ENGINE говорит Django, какую СУБД использовать. Он может принимать одно из значений, описанных в табл. 5.1.

Таблица 5.1. Допустимые значения параметра DATABASE_ENGINE

в Django

Значение

СУБД

Требуемый адаптер

 

 

 

postgresql

PostgreSQL

psycopg версии 1.x, http://www.django­

 

 

project.com/r/python-pgsql/1/

postgresql_psycopg2

PostgreSQL

psycopg версии 2.x, http://www.django­

 

 

project.com/r/python-pgsql/

mysql

MySQL

MySQLdb, http://www.djangoproject.com/r/

 

 

python-mysql/

sqlite3

SQLite

Если используется Python 2.5+, то

 

 

адаптер не нужен. В противном случае –

 

 

pysqlite, http://www.djangoproject.com/r/

 

 

python-sqlite/

oracle

Oracle

cx_Oracle, http://www.djangoproject.com/r/

 

 

python-oracle/

 

 

 

•• Отметим, что, какую бы СУБД вы ни использовали, необходимо загрузить и установить соответствующий адаптер. Все они бесплатные и имеются в Интернете, соответствующие ссылки приведены в столбце «Требуемый адаптер». Если вы работаете в Linux, то, возможно, в дистрибутиве уже имеются нужные пакеты. (Ищите пакеты, называющиеся как-то вроде python-postgresql или pythonpsycopg.) Например:

Настройка базы данных

99

DATABASE_ENGINE = ‘postgresql_psycopg2’

•• Параметр DATABASE_NAME сообщает Django имя вашей базы данных, например:

DATABASE_NAME = ‘mydb’

При работе с SQLite укажите полный путь к файлу базы данных, например:

DATABASE_NAME = ‘/home/django/mydata.db’

В этом примере мы разместили базу данных SQLite в каталоге /home/ django, но можно выбрать любой каталог.

•• Параметр DATABASE_USER сообщает Django имя пользователя, от имени которого устанавливается соединение с базой данных. При работе с SQLite оставьте этот параметр пустым.

•• Параметр DATABASE_PASSWORD сообщает Django пароль для подключения к базе данных. При работе с SQLite или, если вы подключаетесь к серверу без пароля, оставьте этот параметр пустым.

•• Параметр DATABASE_HOST сообщает Django адрес сервера базы данных. Если сервер работает на том же компьютере, где установлен Django (то есть его адрес localhost), оставьте этот параметр пустым. При работе с SQLite он также не задается.

СУБД MySQL – особый случай. Если значение этого параметра начинается с символа слеша (‘/’) и вы работаете с MySQL, то соединение с MySQL будет устанавливаться через указанный UNIX-сокет, например:

DATABASE_HOST = ‘/var/run/mysql’

После того как вы определите эти параметры и сохраните файл settings. py, имеет смысл протестировать конфигурацию. Для этого запустите оболочку командой python manage.py shell, находясь в каталоге проекта mysite. (В предыдущей главе отмечалось, что в этом случае интерпретатор Python запускается с подходящими для Django настройками. В нашем случае это необходимо, потому что Django должен знать, какой файл с параметрами использовать, чтобы получить из него информацию о подключении.)

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

>>>from django.db import connection

>>>cursor = connection.cursor()

Если ничего не произошло, значит, база данных настроена правильно.

Впротивном случае сообщение об ошибке должно дать информацию

отом, что не так. В табл. 5.2 перечислены некоторые типичные ошибки.

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