<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://wiki.linuxformat.ru/wiki/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://wiki.linuxformat.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Shultais</id>
		<title>Linuxformat - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.linuxformat.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Shultais"/>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:Contributions/Shultais"/>
		<updated>2026-05-13T01:30:49Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.19.20+dfsg-0+deb7u3</generator>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/LXF105:Django</id>
		<title>LXF105:Django</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/LXF105:Django"/>
				<updated>2009-08-31T14:52:35Z</updated>
		
		<summary type="html">&lt;p&gt;Shultais: Добавлена ссылка на сайт автора&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;: '''Новая серия!''' Разрабатываем динамические web-приложения современным способом[[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
==''Django'': Новостной портал==&lt;br /&gt;
&lt;br /&gt;
{{Цикл/Django}}&lt;br /&gt;
&lt;br /&gt;
: '''ЧАСТЬ 1''' Нужен ли миру еще один новостной портал? Вряд ли больше, чем очередной интернет-магазин, но кого это останавливало! '''Никита Шультайс''' (http://shultais.ru) расскажет эту старую историю на новый лад.&lt;br /&gt;
&lt;br /&gt;
Сегодня каждый более-менее продвинутый пользователь желает иметь свой сайт. Одни устанавливают готовые системы управления web-содержимым (CMS), вторые пишут все с нуля, а третьи – используют web-каркасы (framework), позволяющие создавать отличные динамические web-сайты без особых усилий.&lt;br /&gt;
''Django'' как раз и является таким каркасом, написанным на ''Python''. К&lt;br /&gt;
числу задач, с которыми он помогает справиться, относятся:&lt;br /&gt;
&lt;br /&gt;
* Создание и обработка форм;&lt;br /&gt;
* Разделение логики и представления с помощью мощной системы шаблонов;&lt;br /&gt;
* Добавление, поиск, извлечение и удаление записей из базы данных, в объектно-ориентированном стиле (ORM);&lt;br /&gt;
* Обработка URL с помощью регулярных выражений;&lt;br /&gt;
* Автогенерация интерфейса администратора (в просторечии, «админки»);&lt;br /&gt;
* Аутентификация и авторизация пользователей;&lt;br /&gt;
* Кэширование как отдельных элементов, так и целых страниц;&lt;br /&gt;
* Интернационализация сайта;&lt;br /&gt;
* Создание и отправка сообщений по e-mail;&lt;br /&gt;
* Тестирование;&lt;br /&gt;
* Работа с сессиям, HTTP-запросами и cookie.&lt;br /&gt;
&lt;br /&gt;
Все эти возможности сочетаются с относительно высокой скоростью работы и простотой написания кода. Ну что, заинтересовались?&lt;br /&gt;
Тогда приступим.&lt;br /&gt;
&lt;br /&gt;
===На исходную!===&lt;br /&gt;
&lt;br /&gt;
Дистрибутив ''Django'' можно скачать с официального сайта (http://www.djangoproject.com) или взять с '''LXFDVD'''. Желательно использовать&lt;br /&gt;
версию из репозитория (0.97), но можно ограничиться и последним стабильным релизом – 0.96. Кроме того, для связи с сервером ''MySQL'' нам&lt;br /&gt;
понадобится ''MySQLdb'' версии 1.2.1p2 или выше. После получения ''Django'',&lt;br /&gt;
распакуйте архив, перейдите в появившийся каталог и выполните&lt;br /&gt;
&lt;br /&gt;
 sudo python setup.py install&lt;br /&gt;
 [ваш пароль]&lt;br /&gt;
&lt;br /&gt;
При желании, можете заменить ''sudo'' на ''su'' и ввести пароль суперпользователя.&lt;br /&gt;
&lt;br /&gt;
Для начала работы нам нужно создать «проект», в котором будут&lt;br /&gt;
храниться все наши файлы. Для этого перейдите в директорию, в&lt;br /&gt;
которой вы собираетесь хранить проект (например, '''/var/www'''), и&lt;br /&gt;
дайте команду&lt;br /&gt;
&lt;br /&gt;
 python /путь/к/django/bin/django-admin.py startproject myproject&lt;br /&gt;
&lt;br /&gt;
после выполнения которой в текущем каталоге появится поддиректория с именем '''myproject'''.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим ее внутреннюю структуру:&lt;br /&gt;
 myproject/&lt;br /&gt;
  __init__.py&lt;br /&gt;
  manage.py&lt;br /&gt;
  settings.py&lt;br /&gt;
  urls.py&lt;br /&gt;
&lt;br /&gt;
'''__init__.py''' – это специальный (как видно по двум подчеркиваниям в&lt;br /&gt;
имени) файл, который указывает на то, что данный каталог является&lt;br /&gt;
модулем (пакетом) языка ''Python''; '''manage.py''' – утилита, позволяющая&lt;br /&gt;
управлять вашим проектом. '''Settings.py''' содержит его настройки, а '''urls.py''' – это так называемый «файл URL-карт». В нем указывается, какому адресу какой код соответствует.&lt;br /&gt;
&lt;br /&gt;
Для запуска проекта выполните из его директории команду&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF105_86_1.jpg|Рис. 1|300px]]Рис. 1. Оно работает, следовательно, существует!|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
 python manage.py runserver&lt;br /&gt;
&lt;br /&gt;
Она запустит встроенный в ''Django'' web-сервер, предназначенный&lt;br /&gt;
специально для разработки сайтов. Сервер стартует на локальном&lt;br /&gt;
хосте на порту 8000 и, если все пройдет успешно, вы увидите в консоли следующее сообщение:&lt;br /&gt;
&lt;br /&gt;
 Validating models...&lt;br /&gt;
 0 errors found.&lt;br /&gt;
 Django version 1.0, using settings 'myproject.settings'&lt;br /&gt;
 Development server is running at http://127.0.0.1:8000/&lt;br /&gt;
 Quit the server with CONTROL-C.&lt;br /&gt;
&lt;br /&gt;
Направив       свой     любимый       web-браузер    по   адресу&lt;br /&gt;
http://127.0.0.1:8000/:, вы получите нечто похожее на рис. 1.&lt;br /&gt;
&lt;br /&gt;
===Настроим ''Django''===&lt;br /&gt;
&lt;br /&gt;
Как уже говорилось выше, основные настройки вашего проекта хранятся в файле '''settings.py'''. Откроем его и найдем строку '''DATABASE_ENGINE = ''''': она отвечает за СУБД, которую мы будем использовать в нашем проекте. Поскольку мы выбрали ''MySQL'', изменим эту строку&lt;br /&gt;
на '''DATABASE_ENGINE = 'mysql''''. Далее, в '''DATABASE_USER''' укажем&lt;br /&gt;
имя пользователя вашей базы данных (заметьте, что у него должны&lt;br /&gt;
быть права на создание таблиц в БД), а в '''DATABASE_PASSWORD''' –&lt;br /&gt;
его пароль. Если сервер баз данных запущен на локальном хосте и на&lt;br /&gt;
порту по умолчанию, параметры '''DATABASE_HOST''' и '''DATABASE_PORT'''&lt;br /&gt;
можно оставить пустыми. В поле '''DATABASE_NAME''' введите имя базы&lt;br /&gt;
данных (не путайте его с именем СУБД!), которую будет использовать&lt;br /&gt;
проект. Да, и не забудьте создать ее перед использованием. В частности, для ''MySQL'' это делается так:&lt;br /&gt;
&lt;br /&gt;
 mysql --user=username --password=password&lt;br /&gt;
 mysql &amp;gt; CREATE DATABASE `myproject_base` DEFAULT CHARACTER&lt;br /&gt;
 SET utf8 COLLATE utf8_general_ci;&lt;br /&gt;
&lt;br /&gt;
Здесь '''username''' – имя пользователя, который имеет доступ к&lt;br /&gt;
СУБД, '''password''' – его пароль, '''myproject_base''' – название БД, в которой будут храниться данные нашего проекта.&lt;br /&gt;
&lt;br /&gt;
Перейдем к секции '''INSTALLED_APPS''', которая отвечает за установленные в нашем проекте приложения. По умолчанию, команда&lt;br /&gt;
startproject создает проект с четырьмя предустановленными приложениями общего назначения:&lt;br /&gt;
&lt;br /&gt;
 INSTALLED_APPS = (&lt;br /&gt;
   'django.contrib.auth',&lt;br /&gt;
   'django.contrib.contenttypes',&lt;br /&gt;
   'django.contrib.sessions',&lt;br /&gt;
   'django.contrib.sites',&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
Каждое из них отвечает за свою специфическую область:&lt;br /&gt;
&lt;br /&gt;
* '''django.contrib.auth''' – система аутентификации;&lt;br /&gt;
* '''django.contrib.contenttypes''' – каркас типов содержимого;&lt;br /&gt;
* '''django.contrib.sessions''' – работа с сессиями;&lt;br /&gt;
* '''django.contrib.sites''' – каркас, позволяющий управлять несколькими сайтами из одной установки ''Django''.&lt;br /&gt;
&lt;br /&gt;
Хотя эти приложения и перечислены в '''INSTALLED_APPS''', они еще не установлены окончательно, так как для них не созданы таблицы в&lt;br /&gt;
базе данных. Для завершения инсталляции, выполните следующий&lt;br /&gt;
код:&lt;br /&gt;
&lt;br /&gt;
 python manage.py syncdb&lt;br /&gt;
&lt;br /&gt;
Данная команда создаст необходимые таблицы в базе данных,&lt;br /&gt;
после чего вам будет предложено определить суперпользователя –&lt;br /&gt;
администратора сайта:&lt;br /&gt;
&lt;br /&gt;
 You just installed Django's auth system, which means you don't have any&lt;br /&gt;
 superusers defined.&lt;br /&gt;
 Would you like to create one now? (yes/no):&lt;br /&gt;
&lt;br /&gt;
Ответьте «'''yes'''», а затем следуйте инструкциям, заполняя поля&lt;br /&gt;
имени, '''e-mail''' и пароля для суперпользователя. Вот теперь система&lt;br /&gt;
полностью установлена.&lt;br /&gt;
&lt;br /&gt;
===Создание приложения===&lt;br /&gt;
&lt;br /&gt;
Перечисленные выше приложения ''Django'' – это, по сути, программы&lt;br /&gt;
''Python'', предназначенные для решения конкретной задачи. Например,&lt;br /&gt;
мы можем написать приложение-блог, которое будет создавать записи в личном интернет-дневнике, а также предоставлять другим пользователям возможность просматривать их. Для генерации нового приложения нужно просто скомандовать:&lt;br /&gt;
&lt;br /&gt;
 python manage.py startapp news&lt;br /&gt;
&lt;br /&gt;
где '''news''' – имя нашего приложения; его-то мы и будем разрабатывать&lt;br /&gt;
на протяжении всех четырех уроков. Задача '''news''' достаточно стандартна: это публикация, просмотр и обсуждение новостей. По традиции, давайте начнем с изучения структуры нашего приложения:&lt;br /&gt;
&lt;br /&gt;
 news/&lt;br /&gt;
   __init__.py&lt;br /&gt;
   models.py&lt;br /&gt;
   views.py&lt;br /&gt;
&lt;br /&gt;
Как видно, оно состоит из трех файлов: '''__init__.py''', с которым&lt;br /&gt;
мы уже встречались выше, '''models.py''' – места для описания моделей&lt;br /&gt;
(схем таблиц в БД с некоторыми дополнительными данными) и '''views.py''', определяющего представления (код, который отвечает за логику&lt;br /&gt;
вашего приложения).&lt;br /&gt;
&lt;br /&gt;
Работа с приложением обычно начинается с создания моделей,&lt;br /&gt;
поэтому откройте файл '''models.py''' в вашем любимом текстовом&lt;br /&gt;
редакторе и наберите следующий код:&lt;br /&gt;
&lt;br /&gt;
 from django.db import models&lt;br /&gt;
 class News(models.Model):&lt;br /&gt;
   title = models.CharField(max_length=70)&lt;br /&gt;
   description = models.CharField(max_length=255)&lt;br /&gt;
   pub_date = models.DateTimeField()&lt;br /&gt;
   text = models.TextField()&lt;br /&gt;
 class Comment(models.Model):&lt;br /&gt;
   news = models.ForeignKey(News)&lt;br /&gt;
   username = models.CharField(max_length=70)&lt;br /&gt;
   text = models.TextField()&lt;br /&gt;
   pub_date = models.DateTimeField(auto_now_add=True)&lt;br /&gt;
&lt;br /&gt;
Мы создали две модели: первая ('''News''') отвечает за саму новость,&lt;br /&gt;
а вторая ('''Comment''') – за комментарии к ней.&lt;br /&gt;
&lt;br /&gt;
Каждая модель (таблица, в терминах БД) представлена классом,&lt;br /&gt;
который наследуется от '''django.db.models.Model'''. Все поля представляются объектом класса '''models.*Field'''. Имя поля используется как&lt;br /&gt;
для создания столбцов в таблице БД, так и для доступа к данным&lt;br /&gt;
через ''Django ORM'', поэтому для упрощения чтения и написания кода&lt;br /&gt;
старайтесь давать полям осмысленные названия.&lt;br /&gt;
&lt;br /&gt;
Начнем с поля '''title''' класса '''News'''. Запись '''models.CharField''' указывает на то, что это поле является символьным, а '''max_length=70''' говорит, что мы можем записать в него максимум 70 символов. Соответственно,&lt;br /&gt;
'''DateTimeField''' означает, что поле будет содержать дату в формате&lt;br /&gt;
'''DateTime''' (атрибут '''auto_now_add''' в '''pub_date''' в классе '''Comments''' указывает на то, что поле при создании записи будет автоматически заполняться текущим временем), '''TextField''' – это обычный текст.&lt;br /&gt;
&lt;br /&gt;
Особое значение имеет '''ForeignKey''', который говорит, что поле&lt;br /&gt;
является внешним ключом (в связке «один-ко-многим») по отношению к модели (таблице) '''News'''. То есть, у каждого комментария&lt;br /&gt;
есть поле news, хранящее номер новости, к которой относится комментарий.&lt;br /&gt;
&lt;br /&gt;
Теперь, когда модели созданы, приложение можно установить.&lt;br /&gt;
Этот процесс проходит в два этапа:&lt;br /&gt;
&lt;br /&gt;
# Инициализация приложения в '''settings.py''' в разделе '''INSTALLED_APPS''';&lt;br /&gt;
# Создание таблиц в базе данных.&lt;br /&gt;
&lt;br /&gt;
Откройте '''settngs.py''' и добавьте к '''INSTALLED_APPS''' строку ''''myproject.news''''. Заметьте, что '''INSTALLED_APPS''' – это кортеж ''Python'',&lt;br /&gt;
поэтому будьте внимательны с синтаксисом. Теперь перейдите в корень нашего проекта и наберите команду:&lt;br /&gt;
&lt;br /&gt;
  python manage.py syncdb&lt;br /&gt;
&lt;br /&gt;
Она, как мы уже знаем, создаст необходимые таблицы в БД.&lt;br /&gt;
&lt;br /&gt;
===Автогенерация «админки»===&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF105_88_1.jpg|Рис. 2|250px]]Рис. 2. Врата в административный раздел ''Django''. |Ширина=250px}}&lt;br /&gt;
&lt;br /&gt;
Одной из замечательных возможностей ''Django'' является автогенерация раздела администрирования сайта. Конечно, в серьезных работающих проектах его использование не вполне удобно, однако на стадии разработки и в небольших проектах он очень полезен.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы подключить систему администрирования, нужно выполнить несколько шагов (в той очередности, в которой они представлены ниже):&lt;br /&gt;
# Добавить '''django.contrib.admin''' в '''INSTALLED_APPS''' вашего проекта.&lt;br /&gt;
# Выполнить ''python manage.py syncdb'' из корневого каталога проекта.&lt;br /&gt;
# Открыть myproject/urls.py и раскомментировать строку&lt;br /&gt;
  # (r'^admin/', include('django.contrib.admin.urls')),&lt;br /&gt;
&lt;br /&gt;
После этого запустите web-сервер (''python manage.py runserver'') и перейдите по адресу http://127.0.0.1:8000/admin/. Вы увидите форму авторизации для входа в систему администрирования:&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF105_88_2.jpg|Рис. 3|300px]]Рис. 3. Административный интерфейс ''Django''.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Введите имя и пароль суперпользователя, и – добро пожаловать&lt;br /&gt;
в «админку» ''Django''! На ее главной странице (рис.3) можно видеть&lt;br /&gt;
несколько областей:&lt;br /&gt;
* Вверху – панель приветствия, просмотра документации, изменения пароля и выхода из системы.&lt;br /&gt;
* Посередине слева – установленные приложения (название и модели) и действия, которые можно совершать с ними (добавление и изменение записей).&lt;br /&gt;
* Посередине справа – последние действия, которые были произведены в «админке».&lt;br /&gt;
&lt;br /&gt;
Вы заметили, что нашего приложения '''news''' в нет списке (в журнале&lt;br /&gt;
этого не видно, но уж поверьте мне на слово)? Все правильно: просто&lt;br /&gt;
так приложения в нем не появляются – для этого нужно выполнить&lt;br /&gt;
несколько несложных действий. Откройте файл моделей ('''models.py''')&lt;br /&gt;
приложения news и добавьте в каждую модель подкласс.&lt;br /&gt;
&lt;br /&gt;
 class Admin:&lt;br /&gt;
   pass&lt;br /&gt;
&lt;br /&gt;
Сохраните файл моделей и перезагрузите страницу. Теперь наше&lt;br /&gt;
приложение должно быть доступно в «админке». Если бы мы использовали какой-то другой HTTP-сервер (например, ''Apache''), нам бы&lt;br /&gt;
пришлось бы перегружать его после каждого изменения в исходных&lt;br /&gt;
кодах. А это очень неудобно, особенно когда процесс разработки в&lt;br /&gt;
самом разгаре.&lt;br /&gt;
&lt;br /&gt;
Ну что, добавим первую новость? Найдите на странице строку&lt;br /&gt;
'''Newss''' (это не опечатка – ''Django'' автоматически добавляет окончание&lt;br /&gt;
'''-s''' к именам моделей) и перейдите по ссылке '''Add'''. Откроется форма для&lt;br /&gt;
добавления новости:&lt;br /&gt;
&lt;br /&gt;
Заполните все поля и нажмите кнопку '''Save''' [Сохранить]. Вы будете перенаправлены на страницу списка доступных новостей. Пока что он состоит из одной новости, которую мы только что добавили, но&lt;br /&gt;
как только их станет больше, возникнет одно неудобство: все новости&lt;br /&gt;
будут отображаться как '''News object''' [Объект класса News], и мы быстро&lt;br /&gt;
запутаемся, где какая. Чтобы видеть заголовок новости, изменим&lt;br /&gt;
'''models.py''' приложения '''news''', добавив в модель '''News''' следующий код:&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF105_88_3.jpg|Рис. 4|300px]]Рис. 4. Форма для добавления новостей.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
 def __unicode__(self):&lt;br /&gt;
   return self.title&lt;br /&gt;
&lt;br /&gt;
Обновите страницу – у новостей появятся осмысленные названия.&lt;br /&gt;
Давайте добавим еще одну новость, но на этот раз оставим поле '''Title'''&lt;br /&gt;
пустым, в поле '''Date''' напишем слово «дата», а в поле '''Time''' – «время»&lt;br /&gt;
и попробуем сохранить ее в базе. Что, ''Django'' ругается? И правильно делает – нечего оставлять поля пустыми. Обратите внимание, что&lt;br /&gt;
прочие поля (которые были введены верно) остались нетронутыми, и&lt;br /&gt;
вам осталось только исправить ошибки, а не заполнять форму заново, как это часто бывает на сайтах «средней руки».&lt;br /&gt;
&lt;br /&gt;
===Публикация в сети===&lt;br /&gt;
&lt;br /&gt;
Хорошо, создавать записи («объекты» в терминах ''Django ORM'') мы научились, а дальше-то что? Дальше нам надо разместить их на сайте – ведь&lt;br /&gt;
от новостей, которые никто не видит, нет пользы. Чтобы сделать это,&lt;br /&gt;
нужно опять пройти через несколько несложных этапов:&lt;br /&gt;
&lt;br /&gt;
* Создать представление (функцию, отвечающую за логику), которое будет выбирать нужные нам объекты из базы данных и обрабатывать их в соответствии с нашими потребностями;&lt;br /&gt;
* Создать шаблон, который будет отвечать за стиль отображения данных;&lt;br /&gt;
* Передать выбранные объекты в шаблон;&lt;br /&gt;
* Связать URL с нашим представлением.&lt;br /&gt;
&lt;br /&gt;
Порядок тут особой роли не играет. К тому же, за разные действия&lt;br /&gt;
могут отвечать разные люди: за представления – программист, за&lt;br /&gt;
шаблоны – дизайнер.&lt;br /&gt;
&lt;br /&gt;
Начнем с представлений. Они, как мы знаем, хранятся в файле&lt;br /&gt;
'''views.py''' в корне каждого приложения. Перейдите в директорию '''news'''&lt;br /&gt;
и откройте файл '''views.py'''. Затем добавьте следующий код (разумеется,&lt;br /&gt;
номера строк приведены исключительно для удобства):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
from datetime import datetime&lt;br /&gt;
from django.template.loader import get_template&lt;br /&gt;
from django.http import HttpResponse&lt;br /&gt;
from django.template import RequestContext&lt;br /&gt;
&lt;br /&gt;
from news.models import News&lt;br /&gt;
&lt;br /&gt;
def last_news(request):&lt;br /&gt;
news = News.objects.filter(&lt;br /&gt;
     pub_date__lte=datetime.now()).order_by(«-pub_date»)[:10]&lt;br /&gt;
template = get_template(“news/last_news.html”)&lt;br /&gt;
context = RequestContext(request, {&lt;br /&gt;
     “last_news”:news,&lt;br /&gt;
})&lt;br /&gt;
return HttpResponse(template.render(context))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первые четыре строчки загружают необходимые функции и классы:&lt;br /&gt;
* '''datetime''' – тип для работы с датой и временем;&lt;br /&gt;
* '''get_template''' – функция, которая ищет и возвращает шаблон;&lt;br /&gt;
* '''HttpResponse''' – основной HTTP ответ;&lt;br /&gt;
* '''RequestContext''' – специальный класс, с помощью которого можно определять переменные, используемые в шаблонах.&lt;br /&gt;
&lt;br /&gt;
Далее (строка 6) происходит импорт класса модели наших новостей. С&lt;br /&gt;
его помощью мы получаем доступ к ''Django ORM'', и, следовательно, к объектам в нашей базе данных.&lt;br /&gt;
&lt;br /&gt;
Функция '''last_news''' – это и есть представление. Заметьте, что она принимает обязательный аргумент '''request''', через который передаются параметры HTTP-запроса.&lt;br /&gt;
&lt;br /&gt;
Что же происходит при вызове '''last_news'''? Сначала мы извлекаем&lt;br /&gt;
объекты из базы данных (строки 9–10). Конструкция '''filter''' ограничивает&lt;br /&gt;
выборку по какому-то условию, в нашем случае '''pub_date__lte=datetime.now()''' означает, что дата публикации новостей должна быть меньше или&lt;br /&gt;
равна ('''lte''' – «less than or equal to») текущей дате ('''datetime.now()'''). Это&lt;br /&gt;
позволит нам создавать новости «на будущее», и они не будут отображаться на сайте до тех пор, пока дата их публикации не станет раньше или&lt;br /&gt;
равной текущей. '''order_by''' – сортирует записи по определенному столбцу,&lt;br /&gt;
в нашем случае – '''pub_date''', а знак минус означает, что сортировка будет&lt;br /&gt;
произведена в обратном порядке. И наконец '''[:10]''' – срез, который указывает, что извлекать нужно только 10 последних записей.&lt;br /&gt;
&lt;br /&gt;
В строке 11 мы получаем объект шаблона, передавая в функцию&lt;br /&gt;
'''get_template''' путь до него. Строки 12–14 отвечают за наполнение наших&lt;br /&gt;
шаблонов какими-то данными, причем мы передаем в шаблон весь HTTP-&lt;br /&gt;
запрос (request) и извлеченные новости (на самом деле, они еще даже&lt;br /&gt;
не извлечены: просто создан объект, с помощью которого новости будут&lt;br /&gt;
получены при первом обращении к ним). Обратите внимание, что данные&lt;br /&gt;
передаются в шаблон с помощью словаря, где ключ – это имя переменной,&lt;br /&gt;
которая будет доступна в шаблоне, а значение – ее значение.&lt;br /&gt;
&lt;br /&gt;
Ну и, наконец, в последней строке мы возвращаем стандартный HTTP-&lt;br /&gt;
ответ, в который передаем готовый заполненный шаблон. Вот только... где&lt;br /&gt;
взять шаблон?&lt;br /&gt;
&lt;br /&gt;
===Шаблоны===&lt;br /&gt;
&lt;br /&gt;
Для начала подготовим базовый шаблон – '''index.html'''. Создадим в корне&lt;br /&gt;
нашего проекта каталог media для хранения статичных файлов (изображений, шаблонов, стилей и т.д.), а уже в '''media''' – подкаталог '''templates''', в&lt;br /&gt;
который и поместим файл '''index.html''' со следующим содержимым:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&amp;lt;title&amp;gt;My site&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
    &amp;lt;body&amp;gt;&lt;br /&gt;
      &amp;lt;nowiki&amp;gt;&amp;lt;a href=“/news/”&amp;gt;News&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
      {% block application %}&lt;br /&gt;
        Welcome to site.&lt;br /&gt;
      {% endblock %}&lt;br /&gt;
    &amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Шаблон – это обычный текстовый файл, содержащий специальные&lt;br /&gt;
конструкции языка шаблонов ''Django''. В '''index.html''' содержится только одна&lt;br /&gt;
конструкция, которая определяет блок '''application''' (строки 6-8). Она называется «тэгом», причем тэг начинается с '''{%''', а заканчивается '''%}'''. Внутри&lt;br /&gt;
этого блока находится приветствие, как это часто бывает на сайтах. ''Django''&lt;br /&gt;
еще не знает, где искать шаблон, поэтому откроем '''settings.py''', найдем кортеж '''TEMPLATE_DIRS''' и добавим в него строку '''/path/to/project/myproject/media/templates/''', где '''/path/to/project''' – путь до вашего проекта; у меня&lt;br /&gt;
это '''/var/www'''.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=Скорая помощь|Содержание=Если вы не увидели ни одной новости (при условии, что они были добавлены), то скорее всего, вы не попали в нужный часовой пояс. Когда мы вызываем функцию '''datetime.now()''', она возвращает время относительно часового пояса, заданного в переменной&lt;br /&gt;
'''TIME_ZONE''' в файле '''settings.py'''. По умолчанию в нем содержится '''America/Chicago'''. Все, что вам нужно – это изменить значение на название вашего часового пояса на английском, например, на '''Asia/Krasnoyarsk'''. Cписок часовых поясов можно найти в директории '''/usr/share/zoneinfo/'''&lt;br /&gt;
в файле '''zone.tab'''. |Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Теперь, когда базовый шаблон готов, перейдем к шаблону новостей.&lt;br /&gt;
Опять же, создадим в директории нашего приложения ('''news''') каталог&lt;br /&gt;
'''templates''', внутри '''templates''' создадим каталог '''news''', а в '''templates/news'''&lt;br /&gt;
добавим файл '''last_news.html'''. Заметьте, что имя файла шаблона совпадает с именем представления, к которому оно относится. Так делать не&lt;br /&gt;
обязательно – это просто правило хорошего стиля.&lt;br /&gt;
&lt;br /&gt;
Сам файл '''last_news.html''' может иметь следующий вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
{% extends “index.html” %}&lt;br /&gt;
&lt;br /&gt;
{% block application %}&lt;br /&gt;
{% for news in last_news %}&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[{{ news.pub_date|date:»d.m.Y»}}]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;strong&amp;gt;{{ news.title }}&amp;lt;/strong&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;p&amp;gt;{{ news.description }}&amp;lt;/p&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
{% endfor %}&lt;br /&gt;
&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Первая строка означает, что шаблон новости расширяет шаблон '''index.html'''. В строке 3 мы открываем блок '''application''' (он, как вы помните,&lt;br /&gt;
идет из '''index.html''') и переопределяем его. В строках 5–8 мы создаем цикл&lt;br /&gt;
по объектам '''last_news''' (которые были переданы в шаблон из нашего представления). В строках 6–8 мы вставляем данные, относящиеся к каждой&lt;br /&gt;
новости, из '''last_news'''. Помимо тэгов, в шаблоны можно помещать переменные, имена которых будут заменены их значениями. Для этого используется конструкция '''&amp;lt;nowiki&amp;gt;{{ имя_переменной }}&amp;lt;/nowiki&amp;gt;'''. В строке 7 мы обращаемся&lt;br /&gt;
к переменной '''news''', но каждая новость имеет несколько полей (в соответствии с тем, что мы определили в '''models.py'''), поэтому мы указываем&lt;br /&gt;
конкретное поле – '''title''', что в конечном счете выведет заголовок новости.&lt;br /&gt;
Особое внимание нужно обратить на запись в строке 6, где мы не просто&lt;br /&gt;
обращаемся к дате публикации новости '''news.pub_date''', но и применяем&lt;br /&gt;
фильтр форматирования '''|date:«d.m.Y»''', который позволяет вывести дату&lt;br /&gt;
в более традиционном для России виде.&lt;br /&gt;
&lt;br /&gt;
Последнее, что нам осталось – это связать URL с представлением.&lt;br /&gt;
Откроем '''urls.py''' и после строки&lt;br /&gt;
&lt;br /&gt;
 (r'^admin/', include('django.contrib.admin.urls')),&lt;br /&gt;
&lt;br /&gt;
добавим&lt;br /&gt;
&lt;br /&gt;
 (r'^news/', 'news.views.last_news').&lt;br /&gt;
&lt;br /&gt;
Данный код означает, что при переходе к http://www.mydomain.ru/news/ будет выполняться представление '''news.views.last_news'''.&lt;br /&gt;
&lt;br /&gt;
Затем допишите в самый конец файла&lt;br /&gt;
&lt;br /&gt;
 urlpatterns += patterns('django.views.generic.simple',&lt;br /&gt;
     (r'^$','direct_to_template', {'template': 'index.html'}),)&lt;br /&gt;
&lt;br /&gt;
Эта запись говорит, что при обращении к корню сервера должен&lt;br /&gt;
использоваться шаблон '''index.html'''.&lt;br /&gt;
&lt;br /&gt;
Ну все, наш сайт готов. Запустите сервер разработчика и перейдите в&lt;br /&gt;
вашем браузере по адресу http://127.0.0.1:8000/. Кликните по ссылке '''News'''&lt;br /&gt;
и получите самые свежие новости! '''LXF'''&lt;/div&gt;</summary>
		<author><name>Shultais</name></author>	</entry>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/LXF106:Django</id>
		<title>LXF106:Django</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/LXF106:Django"/>
				<updated>2009-08-31T14:51:41Z</updated>
		
		<summary type="html">&lt;p&gt;Shultais: Добавлена ссылка на сайт автора&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;: '''''Django''''' Разрабатываем динамические web-приложения современным способом[[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
==Личная блогосфера==&lt;br /&gt;
&lt;br /&gt;
{{Цикл/Django}}&lt;br /&gt;
&lt;br /&gt;
: '''ЧАСТЬ 2''' Новостной портал из прошлого номера журнала легко превратить в мини-блог, предоставив посетителям возможность оставлять комментарии. Но что делать с троллями и спамерами? '''Никита Шультайс''' (http://shultais.ru) во всем разберется.&lt;br /&gt;
&lt;br /&gt;
В прошлый раз мы научились создавать простые ''Django''-приложения, добавили несколько записей в базу данных и вывели на сайт заголовок, дату и описание для последних десяти новостей. А как быть с теми, кто пожелает прочитать текст&lt;br /&gt;
сообщения целиком?&lt;br /&gt;
&lt;br /&gt;
Правильно, нужно снова создавать представления и шаблоны.&lt;br /&gt;
Но для начала, усовершенствуем наши URL-карты.&lt;br /&gt;
&lt;br /&gt;
===Добавим гибкости===&lt;br /&gt;
&lt;br /&gt;
Откройте файл '''urls.py''' (как вы помните, именно он устанавливает&lt;br /&gt;
соответствие между URL-адресами и представлениями) и замените строку&lt;br /&gt;
&lt;br /&gt;
 (r'^news/', 'news.views.last_news')&lt;br /&gt;
&lt;br /&gt;
на&lt;br /&gt;
&lt;br /&gt;
 (r'^news/', include('news.urls'))&lt;br /&gt;
&lt;br /&gt;
Теперь при переходе к '''news/''' будут подгружаться URL-карты,&lt;br /&gt;
относящиеся к приложению. Это позволит нам работать с новостной системой, не выходя за рамки директории news. Далее, нужно&lt;br /&gt;
составить URL-карты приложения: создайте файл '''news/urls.py''' следующего содержания:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
from django.conf.urls.defaults import *&lt;br /&gt;
urlpatterns = patterns('news.views',&lt;br /&gt;
url(r'^$','last_news',name = 'news.last_news'),&lt;br /&gt;
url(r'^(?P&amp;lt;news_id&amp;gt;\d+)/$','news_detail',name = 'news.news_detail'),&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Что происходит при обращении к http://mysite.com/news/? Сначала часть URL ('''news/''') ищется в главном файле URL-карт, где&lt;br /&gt;
обнаруживается соответствие. Затем система считывает URL-карты,&lt;br /&gt;
отвечающие за конкретное приложение (благодаря '''include('news.urls')''') и продолжает поиск, откинув уже найденный шаблон. Так как&lt;br /&gt;
после '''news/''' ничего нет, ''Django'' остановится в строке 3 файла '''news/urls.py''' и выполнит представление '''news.views.last_news''', которое мы&lt;br /&gt;
создали на прошлом уроке.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим '''news/urls.py''' подробнее. Во второй строке есть интересная запись – '''news.views''', которая указывает ''Django'', что нужно&lt;br /&gt;
задействовать представления из одноименного модуля; это позволяет сэкономить место при наборе URL-карт и облегчает чтение&lt;br /&gt;
кода. Строки 3 и 4 – это URL-карты особого формата. Они обернуты функцией '''url()''', принимающей три (точнее сказать – до пяти)&lt;br /&gt;
аргументов:&lt;br /&gt;
&lt;br /&gt;
* первый (позиционный обязательный) – регулярное выражение;&lt;br /&gt;
* второй (позиционный обязательный) – имя представления, которое будет выполняться;&lt;br /&gt;
* третий (именованный необязательный) – имя URL-карты. Оно должно быть уникальным не только для приложения, но и для всего проекта, и в нашем случае состоит из двух частей: имени приложения и представления (это не единственный вариант). Часто возникают ситуации, когда одно представление обслуживает несколько URL-адресов, но имя каждой URL-карты всё равно должно быть уникальным. Скажем, если представление view может добавлять или изменять объекты, в зависимости от URL, то карты могут называться '''app.view.add''' и '''app.view.change'''.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=Скорая помощь|Содержание=Если вы взглянете на наши модели, то увидите, что поля первичного ключа – '''id''' – в них&lt;br /&gt;
нет, однако в примере с тегом '''url''' мы используем '''news.id''', как ни в чем не бывало. Все верно – ''Django'' сам создает первичные ключи для каждой модели.|Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на конструкцию ('''?P&amp;lt;news_id&amp;gt;\d+''') в строке 4.&lt;br /&gt;
Она совпадает с числом ('''\d+'''), захватывает его и помещает в переменную '''news_id''', которая затем передается в представление.&lt;br /&gt;
&lt;br /&gt;
Хотя картам и не обязательно давать имена, это очень сильно упрощает жизнь&lt;br /&gt;
разработчика и делает приложение переносимым. Поэтому откройте файл '''news/templates/news/last_news.html''' и измените&lt;br /&gt;
строку:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;strong&amp;gt;{{ news.title }}&amp;lt;/strong&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
на&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;strong&amp;gt;&lt;br /&gt;
   &amp;lt;a href=”{% url news.news_detail news_id=news.id %}”&amp;gt;{{ news.title }}&amp;lt;/a&amp;gt;&lt;br /&gt;
 &amp;lt;/strong&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В качестве значения атрибута '''href''' мы вставили тег '''url''' системы шаблонов&lt;br /&gt;
''Django''. Ему передаются имена URL-карты&lt;br /&gt;
и объявленной нами переменной – '''news_id'''. Значение '''news_id''' присваивается первичному ключу новости, которую хотим&lt;br /&gt;
просмотреть.&lt;br /&gt;
&lt;br /&gt;
Теперь, когда ''Django'' будет компилировать наш шаблон, '''{% url news.news_detail news_id=news.id %}''' заменится на http://mysite.com/news/1/ для новости с номером 1, и так далее. Если же вы решите модифицировать URL-&lt;br /&gt;
карты, например, переименовав news/ в главном файле URL-карт&lt;br /&gt;
в '''supernews/''', то система автоматически скорректирует все ссылки,&lt;br /&gt;
и сайт продолжит работать. Не забудьте только отредактировать&lt;br /&gt;
файл '''media/templates/index.html''', поправив:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;a href=”/news/”&amp;gt;News&amp;lt;/a&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
на&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;a href=”{% url news.last_news %}”&amp;gt;News&amp;lt;/a&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если вы решите опробовать возможность автоматической генерации URL, не дожидаясь конца урока, то потребуется также добавить в файл '''news/views.py''' представление-заглушку для нового типа URL,&lt;br /&gt;
&lt;br /&gt;
 def news_detail(requets,news_id):&lt;br /&gt;
   pass&lt;br /&gt;
&lt;br /&gt;
В противном случае автогенерация работать не будет.&lt;br /&gt;
&lt;br /&gt;
===Создаем форму===&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF106_79_1.jpg|Рис. 1|300px]]Рис.1. Вот что должно получиться в итоге.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Продолжим развитие нашего приложения. Сейчас мы создадим&lt;br /&gt;
представление и шаблон, с помощью которого можно просматривать детали новости, а также добавлять комментарии. Первым&lt;br /&gt;
делом нужно реализовать форму для комментариев (рис.1), и,&lt;br /&gt;
как вы могли догадаться, ''Django'' поможет нам в этом непростом&lt;br /&gt;
деле. Дистрибутив ''Django'' содержит целых две системы обработки&lt;br /&gt;
форм: '''forms''' (старую) и '''newforms''' (новую). Мы будем пользоваться&lt;br /&gt;
последней, так как она более удобна и имеет больше возможностей. К тому же в версии 1.0 разработчики удалят старую библио&lt;br /&gt;
теку, оставив только '''newforms''' (которая будет переименована в&lt;br /&gt;
'''forms''').&lt;br /&gt;
&lt;br /&gt;
Формы очень удобно хранить в файле '''forms.py''' в директории приложения. Создайте его и наберите код:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
from django import newforms as forms&lt;br /&gt;
class CommentForm(forms.Form):&lt;br /&gt;
username = forms.CharField(label=”Имя”,required=True,&lt;br /&gt;
    widget=forms.TextInput(attrs={&lt;br /&gt;
       'size':'30',&lt;br /&gt;
       'maxlength':'255'}))&lt;br /&gt;
text = forms.CharField(label=”Текст”,required=True,&lt;br /&gt;
    widget=forms.Textarea())&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=Все еще сомневаетесь?|Содержание=Приведем два основных преимущества отделения URL-карт уровня приложений от главной карты сайта:&lt;br /&gt;
&lt;br /&gt;
* '''Переносимость''' Теперь, если мы хотим добавить наше приложение в другой проект, нужно только скопировать директорию с ним в корень проекта, вписать его в '''INSTALLED_APPS''' и включить '''news/urls.py''' в главный файл '''urls.py'''. Если автор приложения расширит функциональность (например, добавит версию для печати), достаточно будет обновить файлы внутри директории '''news''', не заботясь о добавлении или изменении URL-карт.&lt;br /&gt;
* '''Удобная работа с картами'''. В реальных проектах количество приложений может достигать 20 и больше, и каждое из них может содержать 10–20 внутренних карт. Если бы мы оформляли все URL-карты в одном файле, то его размер составил бы около 400 строк, а следовательно, возросла бы вероятность ошибки.|Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Каждая форма представлена классом ''Python'', унаследованным&lt;br /&gt;
от '''forms.Form'''. Она содержит набор полей, являющихся объектами&lt;br /&gt;
класса '''forms.*Field'''. Так, в строках 4-7 определяется поле '''username'''&lt;br /&gt;
типа '''CharField''' – символьное. Атрибут '''required''' указывает, что мы&lt;br /&gt;
не можем оставить его пустым. Далее следует определение «виджета», то есть органа управления, который будет отображаться на&lt;br /&gt;
web-странице. Виджет связан с определенным элементом '''HTML'''-формы, например, '''&amp;lt;select&amp;gt;, &amp;lt;input&amp;gt;''' или '''&amp;lt;textarea&amp;gt;'''. Каждый класс&lt;br /&gt;
поля имеет свой стандартный виджет, но мы можем переопределить или расширить его, добавив дополнительные атрибуты.&lt;br /&gt;
Виджетом по умолчанию для '''CharField''' является '''TextInput (&amp;lt;input type=»text»&amp;gt;)''', здесь мы задаем ему атрибуты '''size''' и '''maxlength'''. В&lt;br /&gt;
строках 8-9 определяется поле '''text''', которое также является символьным, но, поскольку одна строка плохо подходит для ввода&lt;br /&gt;
комментария, мы назначаем '''text''' виджет '''Textarea()''', соответствующий '''HTML'''-тегу '''&amp;lt;textarea&amp;gt;'''. Обратите внимание на атрибуты '''label:''' они содержат поясняющий текст, который выводится около элемента экранной формы. Так как мы используем кириллицу, необходимо указать кодировку – это делается в первой строке. Я предпочитаю UTF-8.&lt;br /&gt;
&lt;br /&gt;
Теперь пришло время заменить и нашу заглушку (если, конечно,&lt;br /&gt;
вы ее создали):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
def news_detail(request, news_id):&lt;br /&gt;
news = News.objects.get(pk=news_id)&lt;br /&gt;
if request.method == 'POST':&lt;br /&gt;
     form = CommentForm(request.POST)&lt;br /&gt;
     if form.is_valid():&lt;br /&gt;
        comment = Comment(&lt;br /&gt;
           news=news,&lt;br /&gt;
           username = form.cleaned_data['username'],&lt;br /&gt;
           text = form.cleaned_data['text'])&lt;br /&gt;
         comment.save()&lt;br /&gt;
         return HttpResponseRedirect(&lt;br /&gt;
             reverse('news.news_detail',kwargs={'news_id':news_id}))&lt;br /&gt;
 else:&lt;br /&gt;
      form = CommentForm()&lt;br /&gt;
&lt;br /&gt;
 comments = Comment.objects.filter(news=news).order_by(“pub_ date”,”id”)&lt;br /&gt;
 comment_count = comments.count()&lt;br /&gt;
&lt;br /&gt;
 template = get_template(“news/news_detail.html”)&lt;br /&gt;
 context = RequestContext(request, {&lt;br /&gt;
      “news”:news,&lt;br /&gt;
      “form”:form,&lt;br /&gt;
      “comment_count”:comment_count,&lt;br /&gt;
      “comments”:comments&lt;br /&gt;
 })&lt;br /&gt;
 return HttpResponse(template.render(context))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сразу стоит обратить внимание на то, что в представление передается дополнительный аргумент '''news_id''', имя которого совпадает с идентификатором переменной, захватываемой при обработке&lt;br /&gt;
регулярного выражения в файле '''news/urls.py'''. Во второй строке&lt;br /&gt;
мы извлекаем новость, а так как нам нужен только один объект,&lt;br /&gt;
то используем для этого функцию '''get()''', передавая ей '''news_id'''&lt;br /&gt;
в качестве именованного аргумента '''pk''' ('''Primary Key''', первичный ключ). Дальше следуют два блока: для '''POST-''' (строки 4-12), и&lt;br /&gt;
'''GET-'''запросов (14). В строке 3 проверяется, как именно произошло&lt;br /&gt;
обращения к данному URL. В случае '''GET''' мы просто создаем объект&lt;br /&gt;
формы с пустыми полями.&lt;br /&gt;
&lt;br /&gt;
Если же данные были получены посредством запроса '''POST''', они&lt;br /&gt;
передаются в форму. Далее идет проверка на корректность (строка&lt;br /&gt;
5): мы убеждаемся, что заполнены все поля. В случае успеха создается объект комментария. Первый параметр – это новость, к которой&lt;br /&gt;
он относится, аргументам '''username''' и '''text''' присваиваются очищенные&lt;br /&gt;
(то есть преобразованные к определенному типу: '''unicode''' для текста,&lt;br /&gt;
'''datetime''' для даты и так далее) данные из полей формы. В 10-й строке&lt;br /&gt;
объект сохраняется в БД, а в строках 11–12 мы осуществляем перенаправление. Функция '''reverse''' – это аналог тега url в шаблонах. Она принимает имя URL-карты, а в словаре '''kwargs''' должны содержаться требуемые аргументы, причем переменной '''news_id''' присваивается номер&lt;br /&gt;
текущей новости... Проще говоря, мы обновляем страницу. Если же&lt;br /&gt;
проверка в строке 5 не проходит, то мы автоматически отправляемся к&lt;br /&gt;
строке 15 с формой, где уже заполнены некоторые поля, а для пустых&lt;br /&gt;
созданы сообщения об ошибках.&lt;br /&gt;
&lt;br /&gt;
Обратимся к строкам 16 и 17. В первой мы извлекаем все комментарии, относящиеся к новости, а во второй получаем их количество, причем функция '''count()''' генерирует '''SQL'''-конструкцию '''COUNT''',&lt;br /&gt;
которая работает очень быстро.&lt;br /&gt;
&lt;br /&gt;
Строки 19-26 рассматривались на прошлом уроке, и сейчас мы&lt;br /&gt;
не будем на них останавливаться. Лучше рассмотрим шаблон '''news/templates/news/news_detail.html''' – он может иметь такой вид:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
{% extends “index.html” %}&lt;br /&gt;
&lt;br /&gt;
{% block application %}&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{ news.title }}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt; {{ news.text }} &amp;lt;/p&amp;gt;&lt;br /&gt;
{% if comment_count %}&lt;br /&gt;
     &amp;lt;h3&amp;gt;Комментариев: {{ comment_count }}&amp;lt;/h3&amp;gt;&lt;br /&gt;
     {% for comment in comments %}&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;strong&amp;gt;{{ comment.username }}&amp;lt;/strong&amp;gt; пишет:&amp;lt;br/&amp;gt;&lt;br /&gt;
      {{ comment.text }}&amp;lt;br/&amp;gt;&lt;br /&gt;
      Дата {{ comment.pub_date|date:”d.m.Y” }}&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
    {% endfor %}&lt;br /&gt;
{% else %}&lt;br /&gt;
     &amp;lt;h3&amp;gt;Комментариев нет&amp;lt;/h3&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;Оставить комментарий:&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;form action=”.” method=”POST”&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;{{ form }}&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;input type=”submit”/&amp;gt;&lt;br /&gt;
&amp;lt;/form&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на строку 7, с которой начинается новый для нас тег – '''{% if %}'''. Он принимает логическую (а поскольку в ''Python'' все числовые значения и строки, которые не равны нулю и не пусты, эквивалентны True, то практически любую) переменную. В строке 17&lt;br /&gt;
расположена вторая часть тега – альтернативный блок '''{% else %}''' и,&lt;br /&gt;
наконец, в строке 19 тег закрывается. Разумеется, альтернативный&lt;br /&gt;
блок можно опустить.&lt;br /&gt;
&lt;br /&gt;
Хотя ''Django'' берет на себя бремя генерации форм, некоторую&lt;br /&gt;
часть кода приходится писать вручную. Так, в строке 22 мы определяем форму, а в строке 23 создаем таблицу, в которую она будет&lt;br /&gt;
помещена. Дело в том, что по умолчанию при вызове объекта формы все виджеты оборачиваются в '''&amp;lt;nowiki&amp;gt;&amp;lt;tr&amp;gt; и &amp;lt;td&amp;gt;&amp;lt;/nowiki&amp;gt;'''. Это поведение можно&lt;br /&gt;
исправить, заменив '''form''' на '''form.as_ul''' или '''form.as_p''', которые которые возвращают ее в виде ненумерованного списка '''&amp;lt;nowiki&amp;gt;(&amp;lt;ul&amp;gt;)&amp;lt;/nowiki&amp;gt;''' и параграфов '''&amp;lt;nowiki&amp;gt;(&amp;lt;p&amp;gt;)&amp;lt;/nowiki&amp;gt;''', соответственно. Кнопка для отправки формы создается&lt;br /&gt;
в строке 24.&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы создали шаблон и представление для просмотра текста новости и добавления комментариев, осталось только&lt;br /&gt;
импортировать необходимые классы и функции в представление&lt;br /&gt;
'''news/views.py''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
   from news.models import News&lt;br /&gt;
   from news.forms import CommentForm&lt;br /&gt;
   from django.http import HttpResponseRedirect&lt;br /&gt;
   from django.core.urlresolvers import reverse&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Не забудьте, что импортировать классы и функции нужно до их&lt;br /&gt;
первого использования.&lt;br /&gt;
&lt;br /&gt;
===Плохой песик, плохой!===&lt;br /&gt;
&lt;br /&gt;
Когда ваши новости комментируют объективно – это прекрасно,&lt;br /&gt;
но мир неидеален и пока не искоренены спам и флуд, вам придется модерировать сообщения, оставленные посетителями. Наша&lt;br /&gt;
система не поддерживает пре-модерацию; конечно, в реальных&lt;br /&gt;
проектах так поступать не стоит. Но и у нас не всё потеряно – мы&lt;br /&gt;
можем:&lt;br /&gt;
&lt;br /&gt;
* Модерировать комментарии из административного интерфейса&lt;br /&gt;
* Делать это прямо с сайта.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF106_81_1.jpg|Рис. 2|300px]]Рис. 2. Пользователи и группы ''Django''.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Первый вариант настолько прост, что мы даже не будем на&lt;br /&gt;
нем останавливаться, но прежде чем перейти ко второму случаю,&lt;br /&gt;
поговорим о системе прав ''Django''. С главной страницы «админки»&lt;br /&gt;
можно перейти в разделы управления пользователями и группами (рис.2).&lt;br /&gt;
&lt;br /&gt;
Каждый пользователь и группа могут иметь определенные&lt;br /&gt;
права доступа, которые описываются в моделях. По умолчанию,&lt;br /&gt;
модель предоставляет три разрешения: добавить, изменить и удалить объект из базы данных. Если вы перейдете на страницу редактирования пользователя в «админке», то в разделе '''Permissions &amp;gt; User permissions''' увидите список прав доступа, причем в левой&lt;br /&gt;
части ('''Available user permissions''') указываются всевозможные права,&lt;br /&gt;
известные системе, а в правой ('''Chosen user permissions''') – разрешения, которыми обладает редактируемый пользователь (рис.3).&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF106_81_2.jpg|Рис. 3|300px]]Рис. 3. Настройка прав доступа.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Каждая строка содержит три части: приложение, модель и право доступа в человеко-понятной форме. Например, '''news | comment | Can add comment''' означает, что пользователь может добавлять комментарии в модель '''comment''' приложения '''news'''. Проверку прав доступа&lt;br /&gt;
нужно осуществлять на уровне представлений или/и шаблонов. На&lt;br /&gt;
уровне моделей она не производится, поэтому комментарии к нашим&lt;br /&gt;
новостям могут добавлять любые посетители, даже не имеющие прав&lt;br /&gt;
на совершение таких действий.&lt;br /&gt;
&lt;br /&gt;
Условно, всех пользователей системы можно разделить на две&lt;br /&gt;
группы: анонимные и авторизованные. Особый случай – это суперпользователь; в ''Django'', как и в Linux, он царь и бог и обладает&lt;br /&gt;
всеми возможными правами. Назначить себя суперпользователем&lt;br /&gt;
можно, установив галочку напротив поля '''Superuser status'''. Отметим&lt;br /&gt;
ещё два поля в разделе '''Permissions''':&lt;br /&gt;
* '''Staff status''' – если эта галочка установлена, пользователь может заходить в «админку» ''Django''.&lt;br /&gt;
* '''Active''' – если флажок снят, посетителю будет отказано в авторизации, даже когда он верно указывает свои имя и пароль.&lt;br /&gt;
&lt;br /&gt;
Создадим еще одно представление, с помощью которого будем удалять нежелательные комментарии:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
@permission_required('news.delete_comment')&lt;br /&gt;
def delete_comment(request, news_id, comment_id):&lt;br /&gt;
comment = get_object_or_404(Comment,pk=comment_id)&lt;br /&gt;
comment.delete()&lt;br /&gt;
return HttpResponseRedirect(reverse('news.news_ detail',kwargs={'news_id':news_id}))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Основным его отличием от виденных нами ранее является проверка прав доступа. Она реализована с помощью декоратора в строке 1 – кстати, не забудьте его импортировать.&lt;br /&gt;
&lt;br /&gt;
 from django.contrib.auth.decorators import permission_required&lt;br /&gt;
&lt;br /&gt;
Права доступа в декораторах определяются именем приложения&lt;br /&gt;
('''news''') и названием разрешения ('''delete_comment'''). Последнее получается автоматически конкатенацией действия ('''delete''') и модели.&lt;br /&gt;
Обратите внимание на строку 3: в ней мы извлекаем из базы данных единственный объект, но используем сокращение '''get_object_or_404()''', которое возвращает страницу 404 в случае отсутствия объекта. Не забудьте импортировать функцию '''get_object_or_404()''' в&lt;br /&gt;
ваше представление до её использования:&lt;br /&gt;
&lt;br /&gt;
 from django.shortcuts import get_object_or_404&lt;br /&gt;
&lt;br /&gt;
Теперь нам нужно связать URL с представлением:&lt;br /&gt;
&lt;br /&gt;
 url(r'^(?P&amp;lt;news_id&amp;gt;\d+)/(?P&amp;lt;comment_id&amp;gt;\d+)/delete/$','delete_comment',name='news.delete_comment'),&lt;br /&gt;
&lt;br /&gt;
Давайте расширим наш шаблон: во-первых, незачем показывать&lt;br /&gt;
форму анонимным пользователям, во-вторых, следует добавить&lt;br /&gt;
ссылку для удаления комментария, если посетитель обладает достаточными правами. Допишите следующий код после строки 10:&lt;br /&gt;
&lt;br /&gt;
    {% if perms.news.delete_comment %}&lt;br /&gt;
      &amp;lt;a href=”{% url news.delete_comment news_id=news.id,comment_id=comment.id %}”&amp;gt;[X]&amp;lt;/a&amp;gt;&lt;br /&gt;
    {% endif %}&lt;br /&gt;
&lt;br /&gt;
Это напоминает использование декораторов, за исключением&lt;br /&gt;
обращения к объекту '''perms'''. Теперь напротив каждого комментария&lt;br /&gt;
будет присутствовать ссылка для его удаления. Далее, заключите&lt;br /&gt;
форму (строки 21-25) в блок '''{% if %}''', чтобы она не отображалась&lt;br /&gt;
неавторизованным пользователям.&lt;br /&gt;
&lt;br /&gt;
 {% if user.is_authenticated %}&lt;br /&gt;
      ...&lt;br /&gt;
 {% endif %}&lt;br /&gt;
&lt;br /&gt;
Помимо '''perms''', содержащего права текущего пользователя, в&lt;br /&gt;
шаблон передается объект '''user''', предоставляющий доступ к прочим реквизитам: имени пользователя, хэшу пароля, данным о текущем состоянии (анонимный или авторизованный). Например, с помощью:&lt;br /&gt;
&lt;br /&gt;
 {% if user.is_superuser %}&lt;br /&gt;
&lt;br /&gt;
можно определить, является ли посетитель суперпользователем.&lt;br /&gt;
&lt;br /&gt;
===Вас взломали!===&lt;br /&gt;
&lt;br /&gt;
Казалось бы, все в порядке, но есть одна тонкость. Спрятав форму&lt;br /&gt;
от глаз неавторизованного пользователя, мы не лишили его возможности «заполнять» ее, генерируя HTTP-заголовки вручную – представление ведь осталось прежним! Чтобы закрыть лазейку, нужно перво-наперво импортировать исключение '''Http404''' (страница не найдена):&lt;br /&gt;
&lt;br /&gt;
 from django.http import Http404&lt;br /&gt;
&lt;br /&gt;
После проверки метода запроса в файле '''news/views.py''' (строка 3) добавьте конструкцию&lt;br /&gt;
&lt;br /&gt;
 if not request.user.is_authenticated(): raise Http404&lt;br /&gt;
&lt;br /&gt;
она выбрасывает исключение, если посетитель неавторизован.&lt;br /&gt;
&lt;br /&gt;
Помимо '''Http404''', можно создавать ответы с различными HTTP-кодами, например, '''HttpResponseForbidden''' (403 – доступ запрещен)&lt;br /&gt;
и так далее. Заметьте, что '''Http404''' – это именно исключение и его&lt;br /&gt;
нужно возбуждать с помощью '''raise''', тогда как остальные ответы&lt;br /&gt;
возвращаются посредством '''return'''.&lt;br /&gt;
&lt;br /&gt;
И, напоследок, выполним еще одну оптимизацию. Так как комментарии могут оставлять только авторизованные пользователи, не&lt;br /&gt;
будем утруждать их вводом своего имени. Система обработки форм&lt;br /&gt;
позволяет динамически заполнять некоторые поля исходными данными. При замене&lt;br /&gt;
&lt;br /&gt;
 form = CommentForm()&lt;br /&gt;
&lt;br /&gt;
на&lt;br /&gt;
&lt;br /&gt;
 form = CommentForm(initial={'username':request.user.username})&lt;br /&gt;
&lt;br /&gt;
поле '''username''' автоматически получит имя текущего авторизованного пользователя.''' LXF'''&lt;/div&gt;</summary>
		<author><name>Shultais</name></author>	</entry>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/LXF107:Django</id>
		<title>LXF107:Django</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/LXF107:Django"/>
				<updated>2009-08-31T14:50:52Z</updated>
		
		<summary type="html">&lt;p&gt;Shultais: Добавлена ссылка на сайт автора&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Цикл/Django}}&lt;br /&gt;
== В гостях хорошо, а дома лучше ==&lt;br /&gt;
: ''ЧАСТЬ 3 В мире существуют языки, отличные от английского, и компьютеры попроще IBM Roadrunner. '''Никита Шультайс''' (http://shultais.ru) покажет, как Django справляется и с тем, и с другим.''&lt;br /&gt;
&lt;br /&gt;
До сих пор мы созерцали «админку» на английском языке,&lt;br /&gt;
однако ее русификация – тоже не проблема. Для этого нужно всего лишь добавить в кортеж MIDDLEWARE_CLASSES в&lt;br /&gt;
файле settings.py следующую строку:&lt;br /&gt;
 'django.middleware.locale.LocaleMiddleware',&lt;br /&gt;
Она вызывает специальный обработчик, который отыскивает&lt;br /&gt;
нужный перевод. Чтобы определить, какой язык требуется, Django&lt;br /&gt;
делает следующее:&lt;br /&gt;
# Ищет в текущей сессии пользователя значение ключа django_language.&lt;br /&gt;
# Если поиск завершился провалом, Django изучает HTTP-заголовок Accept-Language, генерируемый вашим браузером в соответствии с локальными настройками.&lt;br /&gt;
# Наконец, если ничего не было найдено, используется значение переменной LANGUAGE_CODE, установленное в файле settings.py.&lt;br /&gt;
&lt;br /&gt;
Если основной аудиторией вашего сайта будут русскоязычные пользователи, имеет смысл присвоить переменной LANGUAGE_CODE в файле settings.py значение ru.&lt;br /&gt;
&lt;br /&gt;
Основным понятием, которым оперирует Django при интернационализации, являются строки перевода – помеченный особым&lt;br /&gt;
образом текст в ваших исходных кодах или шаблонах. Собственно&lt;br /&gt;
процесс перевода реализуется библиотекой gettext, которая входит в состав Python. Чтобы ваше приложение стало доступным на&lt;br /&gt;
нескольких языках, нужно пройти несколько этапов:&lt;br /&gt;
# Определить строки перевода в исходных текстах и шаблонах.&lt;br /&gt;
# Запустить утилиту make-messages.py, которая найдет все строки перевода и создаст из них языковой файл.&lt;br /&gt;
# Перевести полученный языковой файл.&lt;br /&gt;
# Запустить утилиту mcompile-messages.py, которая скомпилирует языковой файл в формат, пригодный для его дальнейшего использования системой.&lt;br /&gt;
При этом предполагается, что строки перевода используют&lt;br /&gt;
английский язык.&lt;br /&gt;
&lt;br /&gt;
=== Что переводить? ===&lt;br /&gt;
{{Врезка&lt;br /&gt;
|Заголовок=Вниманию полиглотов&lt;br /&gt;
|Содержание=Сессии в Django полностью основаны на cookie, и если они отключены на клиенте, то могут возникнуть проблемы с переключением языков. Решением является Accept-Language: когда браузер&lt;br /&gt;
устанавливает этот HTTP-заголовок, он руководствуется выбранными вами языковыми предпочтениями. Например, в Mozilla&lt;br /&gt;
Firefox они находятся в Правка &amp;gt; Настройки &amp;gt; Дополнительно,&lt;br /&gt;
на вкладке Общее. В разделе Языки, нажмите кнопку Выбрать&lt;br /&gt;
и переместите Английский язык наверх (см. рис. 1). Теперь&lt;br /&gt;
Firefox будет считать, что английский вам роднее, и сообщит об&lt;br /&gt;
этом Django посредством Accept-Language. Не забудьте вернуть&lt;br /&gt;
настройки обратно, дабы не пугаться англоязычных интерфейсов ваших любимых сайтов!&lt;br /&gt;
[[Изображение:LXF107 88 1.png|center|thumb|250px|Рис.1. Укажите язык «админки» Django (и не только) в настройках Firefox.]]&lt;br /&gt;
|Ширина=300px}}&lt;br /&gt;
Для начала давайте разберемся, как помечаются строки перевода&lt;br /&gt;
в исходных текстах приложения. Откройте файл news/views.py. В&lt;br /&gt;
первую очередь, нужно импортировать функцию ugettext() (она, в&lt;br /&gt;
отличии от gettext(), отлично справляется с Unicode):&lt;br /&gt;
 from django.utils.translation import ugettext as _&lt;br /&gt;
Как можно заметить, для импортированной функции мы создали псевдоним: _ (подчеркивание). Это было сделано для повышения удобочитаемости кода, содержащего большое число строк перевода.&lt;br /&gt;
&lt;br /&gt;
Затем, давайте передадим функции _() текст, который&lt;br /&gt;
мы собираемся перевести, и присвоим результат переменной&lt;br /&gt;
application_name:&lt;br /&gt;
 application_name = _(&amp;quot;news&amp;quot;)&lt;br /&gt;
Поместим эту строку в представление last_news, а переменную&lt;br /&gt;
application_name передадим в шаблон:&lt;br /&gt;
&amp;lt;pre&amp;gt;context = RequestContext(request, {&lt;br /&gt;
&amp;quot;last_news&amp;quot;:news,&lt;br /&gt;
&amp;quot;application_name&amp;quot;:application_name&lt;br /&gt;
})&amp;lt;/pre&amp;gt;&lt;br /&gt;
Теперь откройте шаблон news/templates/news/lats_news.html и&lt;br /&gt;
добавьте имя нашего приложения в строку 4:&lt;br /&gt;
&amp;lt;pre&amp;gt;1-3 ...&lt;br /&gt;
4 &amp;lt;h2&amp;gt;{{ application_name }}&amp;lt;/h2&amp;gt;&lt;br /&gt;
5-...&amp;lt;/pre&amp;gt;&lt;br /&gt;
В шаблон news/templates/news/news_detail.html нужно&lt;br /&gt;
импортировать тэги интернационализации. Для этого в строке 2&lt;br /&gt;
напишите:&lt;br /&gt;
 {% load i18n %}&lt;br /&gt;
после чего в шаблоне будет доступен тэг {% trans %} (и не только),&lt;br /&gt;
с помощью которого мы будем отмечать строки перевода. Замените&lt;br /&gt;
текст в строке 5:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;5 &amp;lt;h1&amp;gt;{{ news.title }}&amp;lt;/h1&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
на&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;h1&amp;gt;{% trans &amp;quot;News&amp;quot; %}&amp;lt;br/&amp;gt;{{ news.title }}&amp;lt;/h1&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Думаю, вы уже догадались, как все это работает. В соответствии&lt;br /&gt;
с нашим планом, настало время запустить специальную утилиту и&lt;br /&gt;
создать языковый файл.&lt;br /&gt;
&lt;br /&gt;
=== Вавилонское смешение ===&lt;br /&gt;
Для начала, откройте терминал, перейдите в директорию приложения news и создайте в ней каталог locale:&lt;br /&gt;
 cd news&lt;br /&gt;
 mkdir locale&lt;br /&gt;
Утилита make-messages.py находится в поддиректории bin установки Django. Вызовите ее со следующими параметрами:&lt;br /&gt;
 python /путь/до/django/bin/make-messages.py -l ru&lt;br /&gt;
Если все пройдет удачно, в директории news/locale будут созданы дополнительные каталоги ru/LC_MESSAGES, а в них – языковый&lt;br /&gt;
файл django.po, который и нужно переводить. Откройте его в любом&lt;br /&gt;
текстовом редакторе (для удобства можно пользоваться программой KBabel из KDE). Вы увидите следующее:&lt;br /&gt;
&amp;lt;pre&amp;gt;#: views.py:23&lt;br /&gt;
msgid &amp;quot;news&amp;quot;&lt;br /&gt;
msgstr &amp;quot;&amp;quot;&lt;br /&gt;
#: templates/news/news_detail.html:5&lt;br /&gt;
msgid &amp;quot;News&amp;quot;&lt;br /&gt;
msgstr &amp;quot;&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Первая запись в каждом блоке указывает место, где была найдена строка перевода, далее идет текст, который нужно перевести,&lt;br /&gt;
и наконец, сам перевод (у нас его пока нет). Мы специально создали два варианта слова «news» – с заглавной буквы и с маленькой,&lt;br /&gt;
чтобы наглядно продемонстрировать, что система воспринимает&lt;br /&gt;
их как разные слова. Заполните пустые строки русскими «новости» и «Новости». После того, как с переводом языкового файла&lt;br /&gt;
будет покончено, скомпилируйте его. Для этого воспользуйтесь&lt;br /&gt;
утилитой compile-messages.py, которая опять же входит в состав&lt;br /&gt;
Django:&lt;br /&gt;
 python /путь/до/django/bin/compile-messages.py&lt;br /&gt;
В каталоге LC_MESSAGES появляется скомпилированный языковой файл django.mo. Все готово: наше приложение теперь поддерживает два языка.&lt;br /&gt;
&lt;br /&gt;
=== Смена языка на сайте ===&lt;br /&gt;
Мы уже выяснили, как задать язык для клиентов с отключенными&lt;br /&gt;
cookie, однако далеко не все пользователи догадаются так поступить, да и у большинства ваших посетителей cookie так или иначе&lt;br /&gt;
включены. Значит, чтобы сменить язык сайта, нужно только переписать cookie, которые за это отвечают. И опять Django не оставляет&lt;br /&gt;
нас в беде. Для начала откроем главный файл с URL-картами – urls.&lt;br /&gt;
py, и после строки&lt;br /&gt;
 (r'^news/', include('news.urls')),&lt;br /&gt;
добавим&lt;br /&gt;
 (r'^i18n/', include('django.conf.urls.i18n')),&lt;br /&gt;
Далее, откроем файл settings.py и добавим следующие&lt;br /&gt;
строки:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;TEMPLATE_CONTEXT_PROCESSORS = (&lt;br /&gt;
&amp;quot;django.core.context_processors.auth&amp;quot;,&lt;br /&gt;
&amp;quot;django.core.context_processors.i18n&amp;quot;,&lt;br /&gt;
&amp;quot;django.core.context_processors.request&amp;quot;,&lt;br /&gt;
)&amp;lt;/source&amp;gt;&lt;br /&gt;
Так мы подключим к нашей системе три контекстных процессора,&lt;br /&gt;
которые добавляют в шаблоны глобальные переменные для работы с текущим пользователем, переменные языковых настроек и&lt;br /&gt;
содержимое HTTP-запроса, соответственно. Откройте файл media/templates/index.html и после тэга &amp;lt;body&amp;gt; добавьте:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;/i18n/setlang/&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;input name=&amp;quot;next&amp;quot; type=&amp;quot;hidden&amp;quot; value=&amp;quot;{{ request.PATH_INFO }}&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;select name=&amp;quot;language&amp;quot;&amp;gt;&lt;br /&gt;
{% for lang in LANGUAGES %}&lt;br /&gt;
&amp;lt;option value=&amp;quot;{{ lang.0 }}&amp;quot;&lt;br /&gt;
{% ifequal lang.0 LANGUAGE_CODE %}&lt;br /&gt;
selected=&amp;quot;selected&amp;quot;&lt;br /&gt;
{% endifequal%}&amp;gt;{{ lang.1 }}&amp;lt;/option&amp;gt;&lt;br /&gt;
{% endfor %}&lt;br /&gt;
&amp;lt;/select&amp;gt;&lt;br /&gt;
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Go&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
В строке 1 задается форма, данные из которой будут направлены на встроенное в Django приложение django.views.i18n.set_language, расположенное по адресу /i18n/setlang/ и отвечающее&lt;br /&gt;
за смену языка сайта. Оно принимает POST-запрос, содержащий&lt;br /&gt;
следующие переменные:&lt;br /&gt;
* next – адрес, куда будет перенаправлен пользователь после смены языка. Это, как правило, адрес страницы, на которой мы решили поменять язык. Он содержится в переменной request.PATH_INFO, которую мы передаем в шаблон с помощью контекстного процессора django.core.context_processors.request.&lt;br /&gt;
* language – язык, который был выбран.&lt;br /&gt;
&lt;br /&gt;
[[Изображение:LXF107 90 1.png|thumb|150px|Рис. 2. Многообразие языков, поддерживаемых Django.]]&lt;br /&gt;
Список языков доступен в переменной LANGUAGES, но если&lt;br /&gt;
вы посмотрите на сайт (рис. 2), то увидите огромный перечень,&lt;br /&gt;
из которого нам нужны только два языка. Дело в том, что если&lt;br /&gt;
в settings.py не определена какая-либо переменная, то ее значение автоматически берется из файла настроек Django – django/conf/global_settings.py, а там представлен кортеж LANGUAGES,&lt;br /&gt;
содержащий все языки, поддерживаемые системой: это более 40&lt;br /&gt;
наименований (в том числе, языки, в которых символы пишутся&lt;br /&gt;
справа налево). Чтобы оставить только те из них, которые нам&lt;br /&gt;
действительно необходимы, добавим в файл settings.py следующий кортеж:&lt;br /&gt;
&amp;lt;pre&amp;gt;LANGUAGES = (&lt;br /&gt;
('en', gettext_noop('English')),&lt;br /&gt;
('ru', gettext_noop('Russian')),&lt;br /&gt;
)&amp;lt;/pre&amp;gt;&lt;br /&gt;
не забыв написать перед ним:&lt;br /&gt;
 gettext_noop = lambda s: s&lt;br /&gt;
Теперь, если вы загрузите сайт, вам будут доступны только два&lt;br /&gt;
языка. Вернемся опять к нашей форме: в строке 6 вы найдете новый&lt;br /&gt;
тэг шаблона ifequal – он сравнивает две переданные переменные, и&lt;br /&gt;
если они одинаковы, выполняет код в строке 7.&lt;br /&gt;
&lt;br /&gt;
=== Особенности перевода админки ===&lt;br /&gt;
В отличиe от представлений, где мы вызывали функцию ugettext, в&lt;br /&gt;
«админке» желательно использовать функцию ленивого (отложенного) перевода ugettext_lazy. Ее главное отличие в том, что перевод&lt;br /&gt;
осуществляется не сразу, а в момент использования строки, например, при обработке шаблона в «админке» Django. Импортируйте эту&lt;br /&gt;
функцию в файле news/models.py:&lt;br /&gt;
 from django.utils.translation import ugettext_lazy as _&lt;br /&gt;
Каждое поле модели принимает первым позиционным аргументом свое имя, поэтому строку&lt;br /&gt;
 title = models.CharField(max_length=70)&lt;br /&gt;
можно заменить на&lt;br /&gt;
 title = models.CharField(_('title'),max_length=70)&lt;br /&gt;
В дополнение к имени поля можно передать текст справки.&lt;br /&gt;
Замените на&lt;br /&gt;
 description = models.CharField(help_text=_('Not more than 255 symbols.'),max_length=255)&lt;br /&gt;
И, наконец, если вам уже надоело слово «News» на главной&lt;br /&gt;
странице «админки», измените его на более подходящее. Добавьте&lt;br /&gt;
в класс News подкласс Meta (напомню, что он обеспечивает дополнительные настройки модели), а внутри него объявите переменную,&lt;br /&gt;
отвечающую за множественное число:&lt;br /&gt;
 class Meta:&lt;br /&gt;
 verbose_name_plural = _('news')&lt;br /&gt;
Добавьте каждому полю имя, текст справки (если требуется) и&lt;br /&gt;
форму множественного числа. Перейдите в каталог news и проделайте те же действия по созданию, переводу и компиляции языковых файлов, что я описывал ранее. После всех операций у вас должно получиться что-то вроде изображенного на рис. 3 и 4.&lt;br /&gt;
&lt;br /&gt;
[[Изображение:LXF107 90 2.png|frame|center|Рис. 3. Русифицированная главная страница админки…]]&lt;br /&gt;
[[Изображение:LXF107 90 3.png|frame|center|Рис. 4. ...и форма добавления новостей.]]&lt;br /&gt;
&lt;br /&gt;
=== Кэширование ===&lt;br /&gt;
Наиболее слабыми звеньями работы сайта являются доступ к базе&lt;br /&gt;
данных и компилирование шаблонов, поэтому стоит сразу задуматься о кэшировании данных. Django прекрасно справляется этой&lt;br /&gt;
задачей и позволяет создавать и хранить кэш самыми различными&lt;br /&gt;
способами и в самых разных местах:&lt;br /&gt;
* В оперативной памяти. Такая возможность достигается с помощью программы Memcached, которая служит промежуточным звеном между данными в памяти и Django. Несомненно, этот способ является наибыстрейшим, однако ваш сервер должен обладать достаточным количеством памяти, чтобы хватило и для Django, и для кэша.&lt;br /&gt;
* В базе данных. В этом случае о скорости доступа к данным нельзя говорить однозначно, так как тут нужно учитывать настройки самой БД; однако если все сделано с умом, можно ожидать хороших результатов.&lt;br /&gt;
* В файловой системе. Самый простой вариант. Его мы и будем использовать.&lt;br /&gt;
Откроем файл settigns.py и объявим в нем переменную:&lt;br /&gt;
 CACHE_BACKEND = 'file:///путь/до/кэша'&lt;br /&gt;
Слово file означает, что мы собираемся использовать файловую&lt;br /&gt;
систему (против memcached и db для оперативной памяти и СУБД,&lt;br /&gt;
соответственно), далее идут три слэша и путь до директории, в которой вы собираетесь хранить данные.&lt;br /&gt;
&lt;br /&gt;
{{Врезка&lt;br /&gt;
|Заголовок=Уровнем ниже&lt;br /&gt;
|Содержание=Низкоуровневое кэширование позволяет хранить любые данные, от переменных и экземпляров классов до фрагментов&lt;br /&gt;
шаблонов и баз данных. Для работы с ним нужно импортировать в представление модуль cache:&lt;br /&gt;
 from django.core.cache import cache&lt;br /&gt;
В большинстве случаев вам будет достаточно пользоваться тремя функциями – cache.set для создания кэша, cache.&lt;br /&gt;
get для извлечения данных, cache.delete для удаления кэша.&lt;br /&gt;
Рассмотрим их поподробнее:&lt;br /&gt;
* cache.set('name', 'value', 60) принимает три параметра: имя кэша, значение и время хранения.&lt;br /&gt;
* cache.get('name') – принимает только имя.&lt;br /&gt;
* cache.delete('name') – только имя.&lt;br /&gt;
|Ширина=300px}}&lt;br /&gt;
Django предоставляет высокоуровневые механизмы для генерации кэша и извлечения его из базы данных. Так, самым верхним&lt;br /&gt;
объектом является кэш всех страниц сайта. Однако если на странице&lt;br /&gt;
находятся динамические данные, которые индивидуальны для каждого пользователя, например, строка приветствия («Здравствуйте,&lt;br /&gt;
Имя»), то все посетители будут видеть приветствие для пользователя, который зашел на сайт самым первым. Если же таких элементов нет, это – самый быстрый способ создать кэш сайта. Чтобы&lt;br /&gt;
включить его, достаточно добавить строку 'django.middleware.cache.CacheMiddleware' в кортеж MIDDLEWARE_CLASSES в файле настроек. Она должна стоять самой первой в кортеже. На нашем сайте нет&lt;br /&gt;
приветствия, однако есть возможность смены языков, и после того&lt;br /&gt;
как кэш страницы создан для одного языка, он будет отображаться&lt;br /&gt;
и для другого.&lt;br /&gt;
&lt;br /&gt;
Следующим глобальным объектом для кэширования является&lt;br /&gt;
представление. И опять, здесь мы сталкиваемся с теми же недостатками, что и при кэшировании всех страниц сайта, с единственным&lt;br /&gt;
отличием: мы сами вправе выбирать, какие страницы будут кэшироваться, а какие – нет. Вы можете импортировать в ваше представление декоратор cache_page:&lt;br /&gt;
 from django.views.decorators.cache import cache_page&lt;br /&gt;
и применить его к какому-либо представлению, например:&lt;br /&gt;
 @cache_page(60 * 15)&lt;br /&gt;
 def last_news(request):&lt;br /&gt;
 ...&lt;br /&gt;
Декоратор принимает единственный параметр – время жизни&lt;br /&gt;
кэша в секундах.&lt;br /&gt;
&lt;br /&gt;
Аналогично можно кэшировать и шаблоны. Откройте файл&lt;br /&gt;
news/templates/news/last_news.html и добавьте после {% extends&lt;br /&gt;
«index.html» %} строку:&lt;br /&gt;
 {% load cache %}&lt;br /&gt;
далее, перед {% for news in last_news %} добавьте&lt;br /&gt;
 {% cache 31536000 last_news LANGUAGE_CODE %}&lt;br /&gt;
а после {% endfor %}:&lt;br /&gt;
 {% endcache %}&lt;br /&gt;
Сначала мы подключаем дополнительные тэги для кэширования,&lt;br /&gt;
затем вызываем тэг {% cache %} с тремя параметрами (вообще-то&lt;br /&gt;
достаточно двух). Первый – время жизни кэша в секундах (у нас&lt;br /&gt;
это целый год), второй – название, а третий – тоже название, но в&lt;br /&gt;
нашем случае это переменная, которая вычисляется динамически, а&lt;br /&gt;
следовательно, для разных языков у нас создается свой кэш. То же&lt;br /&gt;
самое можно сделать и для приветствия.&lt;br /&gt;
&lt;br /&gt;
=== Сигналы ===&lt;br /&gt;
Кэширование позволяет сделать сайт очень быстрым, но как&lt;br /&gt;
быть, если вы определили время жизни кэша в три дня и добавили новость через два? Ведь пока кэш жив, система будет отображать именно его, и нашим посетителям придется ждать целый&lt;br /&gt;
день. С другой стороны, ставить слишком маленький промежуток&lt;br /&gt;
тоже нет смысла – тогда от системы кэширования не будет пользы. Предугадать невозможно, да и незачем. Оптимальный вариант – удалять кэш при наступлении какого-либо события (новость&lt;br /&gt;
добавлена, новость удалена и т.д.). Уведомлением о событии служит сигнал. Создадим внутри директории news файл signals.py&lt;br /&gt;
следующего содержания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
from django.core.cache import cache&lt;br /&gt;
from settings import LANGUAGES&lt;br /&gt;
&lt;br /&gt;
def delete_last_news_cache():&lt;br /&gt;
for lang in LANGUAGES:&lt;br /&gt;
cache.delete('last_news:'+ lang[0])&amp;lt;/source&amp;gt;&lt;br /&gt;
Обратите внимание на строку 6, где мы генерируем имя кэша.&lt;br /&gt;
Здесь учтено, что Django соединяет все, что было передано тэгу {%&lt;br /&gt;
cache %}, символом двоеточия. Поскольку мы не знаем, для каких&lt;br /&gt;
языков был создан кэш, то удаляем все возможные варианты – в&lt;br /&gt;
нашем случае их всего два.&lt;br /&gt;
&lt;br /&gt;
Как же сделать, чтобы этот код выполнялся? Проще простого. Откроем файл news/models.py и добавим в начало следующие&lt;br /&gt;
строки:&lt;br /&gt;
 from django.db.models import signals&lt;br /&gt;
 from django.dispatch import dispatcher&lt;br /&gt;
 from news.signals import delete_last_news_cache&lt;br /&gt;
а в конец:&lt;br /&gt;
 dispatcher.connect(delete_last_news_cache,signal=signals.post_save,sender=News)&lt;br /&gt;
 dispatcher.connect(delete_last_news_cache,signal=signals.post_delete,sender=News)&lt;br /&gt;
Теперь в системе зарегистрированы два сигнала от передатчика&lt;br /&gt;
(sender) News. Первый сигнал генерируется после сохранения объекта в базе данных, то есть при его создании или изменении, а второй – после удаления. В обоих случаях выполняется функция delete_last_news_cache, определенная выше. Вообще, для обращения к&lt;br /&gt;
базе данных в Django существует семь сигналов: pre_init, post_init,&lt;br /&gt;
pre_save, post_save, pre_delete, post_delete и post_syncdb, названия&lt;br /&gt;
которых говорят сами за себя.&lt;br /&gt;
&lt;br /&gt;
=== И сверх этого ===&lt;br /&gt;
Кроме возможности создавать кэш ваших данных, Django поддерживает и другие способы оптимизации. Чтобы сделать ваш сайт еще,&lt;br /&gt;
быстрее нужно добавить в кортеж MIDDLEWARE_CLASSES в файле&lt;br /&gt;
settings.py следующие строки:&lt;br /&gt;
 'django.middleware.http.ConditionalGetMiddleware',&lt;br /&gt;
 'django.middleware.gzip.GZipMiddleware',&lt;br /&gt;
Первая из них включает поддержку условных GET-запросов&lt;br /&gt;
(использование ETag и Last-Modified в HTTP-заголовках), а вторая&lt;br /&gt;
отвечает за сжатие содержимого для браузеров, которые поддерживают такую возможность (а это все современные реализации).&lt;/div&gt;</summary>
		<author><name>Shultais</name></author>	</entry>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/LXF108:Django</id>
		<title>LXF108:Django</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/LXF108:Django"/>
				<updated>2009-08-31T14:49:52Z</updated>
		
		<summary type="html">&lt;p&gt;Shultais: Добавлена ссылка на сайт автора&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Цикл/Django}}&lt;br /&gt;
== Финальные штрихи ==&lt;br /&gt;
: ''ЧАСТЬ 4 Уроки Django подходят к концу, наступает пора экзаменов – если не для вас, то для приложений уж точно. '''Никита Шультайс''' (http://shultais.ru) разберется с тестированием и пробежится по другим возможностям этого каркаса.''&lt;br /&gt;
&lt;br /&gt;
Новостная лента готова – но оправдает ли она ожидания&lt;br /&gt;
пользователей? Ответ на этот вопрос может дать тестирование, лучше всего – в реальных условиях; но какие-то&lt;br /&gt;
основные вещи можно проверить еще на этапе разработки. Если вы&lt;br /&gt;
успели заглянуть в учебник [[LXF108:Rails|Rails]], то уже знаете о методологии TDD. Мы, однако, пойдем другим путем, и будем тестировать&lt;br /&gt;
приложение не до написания, а после.&lt;br /&gt;
&lt;br /&gt;
Дело это – серьезное и сложное, так как нам нужно учитывать&lt;br /&gt;
взаимодействие с БД, компиляцию шаблонов, обработку GET- и&lt;br /&gt;
POST-запросов и прочие компоненты системы: сбой в любом из&lt;br /&gt;
них может вызвать нарушить работу всего сайта. К данной задаче&lt;br /&gt;
можно подойти с двух сторон:&lt;br /&gt;
* Тестирование в браузере. Речь идет о программах Twill (http://twill.idyll.org) и Selenium (http://selenium.openqa.org): они «запоминают» последовательность ваших действий для каждой страницы, а затем воспроизводят ее по запросу. Например, можно ввести в поля формы заведомо неверные данные, получить ожидаемую ошибку и повторять этот тест при каждом серьезном изменении в коде вашего приложения.&lt;br /&gt;
* Тестирование на сервере. И тут Django не оставляет нас на произвол судьбы, предлагая сразу два варианта: doctest (тестирование через документацию) и unittest (модульное тестирование), плюс специальный клиент для отправки GET- и POST-запросов.&lt;br /&gt;
&lt;br /&gt;
Если вы давно программируете на Python, то вам, наверное,&lt;br /&gt;
будет ближе doctest, а мигрантам из мира Java скорее придется по&lt;br /&gt;
вкусу unittest. Никаких ограничений на их использование не накладывается: вы можете выбрать одну систему или применять обе сразу. Мы же остановимся на doctest.&lt;br /&gt;
&lt;br /&gt;
=== Документируй это! ===&lt;br /&gt;
Строка документации в Python – это обычный текст, размещаемый&lt;br /&gt;
после определения функции или класса непосредственно в исходном&lt;br /&gt;
коде. Она же предоставляет содержимое для атрибута __doc__. Как&lt;br /&gt;
правило, ее помещают в тройные кавычки (&amp;quot;&amp;quot;&amp;quot;), что позволяет вводить&lt;br /&gt;
сложные конструкции с переносами строк, отступами, теми же кавычками и... тестами. Этим мы и воспользуемся.&lt;br /&gt;
&lt;br /&gt;
Тесты могут находится в файлах моделей (models.py) – для проверки последних – и в специальных файлах tests.py, расположенных&lt;br /&gt;
в директории приложения. К примеру, создайте файл news/tests.py&lt;br /&gt;
такого содержания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from news.models import News&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from datetime import datetime&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; news = News(title=&amp;quot;Заголовок&amp;quot;,description=&amp;quot;Описание&amp;quot;,pub_date=datetime.now(), text=&amp;quot;Текст&amp;quot;)&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; news.save()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; news = News.objects.get(pk=1)&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; print news.title.encode('utf-8')&lt;br /&gt;
 Заголовок&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
В строке 1 задается кодировка, а начиная с третьей идет сам&lt;br /&gt;
тест. Заметьте, что каждая строка начинается с трех знаков «больше» (&amp;gt;&amp;gt;&amp;gt;), как в интерактивном режиме работы Python. В строке 10&lt;br /&gt;
этих знаков нет, так как она содержит ожидаемый вывод команды&lt;br /&gt;
print со строки 9.&lt;br /&gt;
&lt;br /&gt;
[[Изображение:LXF108 85 1.png|thumb|200px|Рис. 1. Тест пройден удачно!]]&lt;br /&gt;
Перед тем, как запустить тест, в settings.py нужно добавить&lt;br /&gt;
строку&lt;br /&gt;
 TEST_DATABASE_CHARSET=&amp;quot;UTF8&amp;quot;&lt;br /&gt;
чтобы кодировки файла и базы данных совпадали. Перед выполнением теста Django создаст специальную вспомогательную БД, поэтому&lt;br /&gt;
пользователь, указанный в settings.DATABASE_USER, должен иметь&lt;br /&gt;
соответствующие права. Для начала тестирования введите команду:&lt;br /&gt;
 python manage.py test news&lt;br /&gt;
после выполнения которой вы увидите примерно то, что показано&lt;br /&gt;
на рис. 1.&lt;br /&gt;
&lt;br /&gt;
Сообщения похожи на появляющиеся во время создания таблиц&lt;br /&gt;
при первой установке, но теперь все происходит в тестовой БД. В&lt;br /&gt;
конце отображается число выполненных тестов, их результаты и&lt;br /&gt;
уведомление об уничтожении тестовой базы. Мы проверяли наше&lt;br /&gt;
приложение (news), но, как вы помните, Django содержит несколько&lt;br /&gt;
собственных приложений и представлений (например, «админку») –&lt;br /&gt;
и они тоже снабжаются своими тестами. Чтобы выполнить их все,&lt;br /&gt;
нужно ввести команду:&lt;br /&gt;
 python manage.py test&lt;br /&gt;
добавив предварительно в главный файл URL-карт следующие&lt;br /&gt;
строки:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;urlpatterns += patterns('django.contrib.auth.views',&lt;br /&gt;
url(r'^auth/password_reset/$','password_reset'),&lt;br /&gt;
)&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь мы подключаем одно из встроенных представлений, предназначенное для восстановления пароля. Это необходимо, так как при&lt;br /&gt;
тестировании всего проекта происходит обращение по указанному URL,&lt;br /&gt;
и если представление не определено, тест будет провален. Кстати, вы&lt;br /&gt;
тоже можете попробовать password_reset в работе (рис. 2).&lt;br /&gt;
[[Изображение:LXF108 85 2.png|frame|center|Рис. 2. Забыли пароль? Это проблемы Django!]]&lt;br /&gt;
&lt;br /&gt;
=== Имитатор Сети ===&lt;br /&gt;
Количество тестов уже достигло шести, но помимо создания и&lt;br /&gt;
извлечения объектов из базы, нам нужно проверить реакцию на&lt;br /&gt;
GET- и POST-запросы. Как вы знаете, для этих целей существует&lt;br /&gt;
специальный клиент: он эмулирует запрос и возвращает переменные, которые передаются в шаблон для данного URL. Добавьте в&lt;br /&gt;
файл tests.py после строки 10 следующий код:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot; line start=&amp;quot;11&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from django.contrib.auth.models import User&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; user = User.objects.create_user('django_guru', 'user@example.com', 'password')&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; user.save()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from django.test.client import Client&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; c = Client()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; c.login(username='django_guru',password=“password”)&lt;br /&gt;
True&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response = c.get('/news/1/')&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response.status_code&lt;br /&gt;
200&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; print response.context[0]['user'].username&lt;br /&gt;
django_guru&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response = c.post('/news/1/',{'username':“testuser”,'text':»»})&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response.status_code&lt;br /&gt;
200&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response = c.post('/news/1/',{'username':“testuser”,'text':»Comment text»})&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; response.status_code&lt;br /&gt;
302&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from news.models import Comment&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; comment = Comment.objects.get(news=news)&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; print comment.text&lt;br /&gt;
Comment text&amp;lt;/source&amp;gt;&lt;br /&gt;
Разберемся, что здесь происходит. В строках 11–13 мы создаем&lt;br /&gt;
нового пользователя (django_guru), а в 14–15 – тестовый клиент. В&lt;br /&gt;
строке 16 django_guru авторизуется, и отныне все действия в системе&lt;br /&gt;
будут совершаться от его имени. В строке 18 мы переходим на страницу нашей первой новости, передав средствами клиента GET-запрос.&lt;br /&gt;
Для проверки, что нам это удалось, мы изучаем код ответа сервера&lt;br /&gt;
(строка 19) – он должен равняться 200, или тест будет провален.&lt;br /&gt;
Затем (строки 21–22), чтением дополнительных данных ответа, мы&lt;br /&gt;
убеждаемся, что запрос сделал зарегистрированный пользователь&lt;br /&gt;
django_guru. Теперь самое время оставить комментарий – не зря же&lt;br /&gt;
мы авторизовались? В строке 23 генерируется POST-запрос (второй&lt;br /&gt;
аргумент метода post() – словарь отсылаемых на сервер данных).&lt;br /&gt;
Обратите внимание, что значение ключа text оставлено пустым, а&lt;br /&gt;
значит, комментарий не добавится, однако сервер по-прежнему должен возвращать код 200 (строка 25). А вот в строке 26 мы передаем&lt;br /&gt;
все необходимые данные, и так как после создания комментария нас&lt;br /&gt;
перенаправляют на страницу новости, код ответа должен быть равен&lt;br /&gt;
302 (Требуемый URL перемещен). В строках 29–32 проверяется, что&lt;br /&gt;
комментарий был действительно добавлен: мы сравниваем его текст&lt;br /&gt;
с исходным значением. Уфф... тест пройден.&lt;br /&gt;
&lt;br /&gt;
=== Действительно простая синдикация ===&lt;br /&gt;
Какой новостной сайт без ленты? RSS и/или Atom есть везде – будут&lt;br /&gt;
и у нас, а Django нам в этом поможет. Откройте главный файл URL-карт и добавьте в его конец следующие строки:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
from feeds import LatestNews&lt;br /&gt;
&lt;br /&gt;
feeds = {&lt;br /&gt;
'latest': LatestNews,&lt;br /&gt;
}&lt;br /&gt;
urlpatterns += patterns('',&lt;br /&gt;
(r'^feeds/(?P&amp;lt;url&amp;gt;.*)/$', 'django.contrib.syndication.views.feed',&lt;br /&gt;
{'feed_dict': feeds}),&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Далее нужно приготовить ленту LatestNews, которую мы импортируем в строке 1. Создайте в корне проекта каталог feeds с файлом&lt;br /&gt;
__init__.py следующего содержания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
from django.contrib.syndication.feeds import Feed&lt;br /&gt;
from news.models import News&lt;br /&gt;
from django.contrib.syndication.feeds import FeedDoesNotExist&lt;br /&gt;
&lt;br /&gt;
class LatestNews(Feed):&lt;br /&gt;
title = &amp;quot;Последние новости нашего сайта&amp;quot;&lt;br /&gt;
description = &amp;quot;Последние события на сайте mysite.com&amp;quot;&lt;br /&gt;
link = &amp;quot;http://127.0.0.1:8000/news/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
def items(self):&lt;br /&gt;
return News.objects.order_by(&amp;quot;-pub_date&amp;quot;)[:5]&lt;br /&gt;
&lt;br /&gt;
def item_link(self, obj):&lt;br /&gt;
from django.core.urlresolvers import reverse&lt;br /&gt;
return 'http://127.0.0.1:8000%s' % reverse('news.news_detail',kwargs={&amp;quot;news_id&amp;quot;:obj.pk})&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[[Изображение:LXF108 86 1.png|thumb|250px|Рис. 3. Firefox предлагает подписаться на обновления нашего новостного сайта – теперь держитесь!]]&lt;br /&gt;
Поля title, description и link класса LatestNews являются обязательными и отвечают за одноименные элементы RSS. Метод items()&lt;br /&gt;
передает в ленту требуемые объекты, а item_link() отвечает за ссылку&lt;br /&gt;
на сайт. Теперь создайте каталог feeds в media/templates и добавьте&lt;br /&gt;
в него два файла, latest_description.html и latest_title.html: они будут&lt;br /&gt;
отвечать за вид новостной ленты. В lates_description.html напишите:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{ obj.description }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
а в latest_title.html:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[{{ obj.pub_date|date:&amp;quot;d.m.Y&amp;quot; }}] {{ obj.title }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Объект obj представляет собой запись из выборки, которую мы&lt;br /&gt;
возвращаем в строке 13 файла feeds/__init__.py. Пройдя по адресу&lt;br /&gt;
http://127.0.0.1:8000/feeds/latest/, мы увидим предложение Firefox&lt;br /&gt;
сохранить ленту новостей. Пользователи KDE, вероятно, предпочтут&lt;br /&gt;
Akregator – с ним тоже нет никаких проблем (рис. 3, 4)&lt;br /&gt;
&lt;br /&gt;
=== Общие представления ===&lt;br /&gt;
Чтобы облегчить жизнь web-разработчика, Django включил большое&lt;br /&gt;
число представлений для решения стандартных задач. Так, добавив&lt;br /&gt;
в главный файл URL-карт следующий код:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from django.views.generic.list_detail import object_list&lt;br /&gt;
from news.models import News&lt;br /&gt;
urlpatterns += patterns('',&lt;br /&gt;
('^lastnews/$', object_list, {&lt;br /&gt;
'queryset': News.objects.all().order_by('-pub_date')[:10],&lt;br /&gt;
'template_name':'news/last_news.html',&lt;br /&gt;
'template_object_name':'last_news'})&lt;br /&gt;
)&amp;lt;/source&amp;gt;&lt;br /&gt;
а также заменив в файле news/templates/news/last_news.html&lt;br /&gt;
 {% for news in last_news %}&lt;br /&gt;
на&lt;br /&gt;
 {% for news in last_news_list %}&lt;br /&gt;
мы сможем просматривать последние новости по адресу&lt;br /&gt;
http://127.0.0.1:8000/lastnews/, не вызывая представление news.last_news. Чтобы сделать доступными оба варианта, нужно найти в представлении news.last_news строку&lt;br /&gt;
 &amp;quot;last_news&amp;quot;:news,&lt;br /&gt;
и заменить ее на&lt;br /&gt;
 &amp;quot;last_news_list&amp;quot;:news,&lt;br /&gt;
Как вы уже догадались, общее представление object_list предназначено для работы со списком объектов. Еще есть представления для&lt;br /&gt;
вывода объектов в зависимости от даты (django.views.generic.date_based.*), что позволяет очень просто создавать архивы записей:&lt;br /&gt;
* archive_index – вывод последних объектов, добавленных в базу данных;&lt;br /&gt;
* archive_{year,month,week,day,today} – вывод всех объектов за определенный год, месяц, неделю, день или за сегодня;&lt;br /&gt;
* object_detail – вывод одного объекта за определенный день.&lt;br /&gt;
Доступны общие представления для создания, обновления и&lt;br /&gt;
удаления объектов. Все они работают немного быстрее, чем созданные вручную, но позволяют решать лишь самые элементарные задачи. Если данные в вашем приложении выбираются из нескольких&lt;br /&gt;
таблиц и это сопровождается расчетами, то общие представления&lt;br /&gt;
не помогут – на то они и общие.&lt;br /&gt;
&lt;br /&gt;
=== Добавляем переменные на лету ===&lt;br /&gt;
В глубинах Django скрываются глобальные контекстные процессоры, основная задача которых – снабжать шаблоны переменными&lt;br /&gt;
и объектами. Узнать, какие из них подключены, можно в кортеже&lt;br /&gt;
TEMPLATE_CONTEXT_PROCESSORS в файле settings.py. Например, у&lt;br /&gt;
нас сейчас работают следующие процессоры:&lt;br /&gt;
* auth – информация о пользователе: объект user, его права доступа и сообщения, которые были ему отправлены;&lt;br /&gt;
* i18n – сведения о текущем языке сайта и клиента;&lt;br /&gt;
* request – данные о запросе.&lt;br /&gt;
Кроме них, существует еще процессор debug, передающий в&lt;br /&gt;
шаблон данные о выполненных SQL-запросах, плюс мы можем&lt;br /&gt;
написать свой собственный! Для этого создадим в корне нашего&lt;br /&gt;
проекта каталог processors и добавим в него два файла: __init__.py&lt;br /&gt;
и context_processors.py. Последний должен содержать такой код:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import settings&lt;br /&gt;
def site_settings(request):&lt;br /&gt;
return {'SETTINGS': settings}&amp;lt;/source&amp;gt;&lt;br /&gt;
Чтобы подключить процессор, просто перечислите его в кортеже&lt;br /&gt;
TEMPLATE_CONTEXT_PROCESSORS. Проверим работоспособность:&lt;br /&gt;
добавим в шаблон news.html следующее:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{ SETTINGS.TIME_ZONE }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Конечно, TIME_ZONE можно заменить на любую другую переменную, определенную в settings.py.&lt;br /&gt;
&lt;br /&gt;
=== Сам себе фильтр ===&lt;br /&gt;
С фильтрами мы познакомились еще в [[LXF105:Django|LXF105]], однако часто возникают ситуации, когда поставляемых с Django вариантов недостаточно. Чтобы написать свой собственный фильтр, создайте в корне&lt;br /&gt;
проекта каталог templatetags/ и добавьте в него файлы __init__.py&lt;br /&gt;
и filters.py. В filters.py напишите:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot; line=&amp;quot;GESHI_NORMAL_LINE_NUMBERS&amp;quot;&amp;gt;&lt;br /&gt;
from django import template&lt;br /&gt;
&lt;br /&gt;
register = template.Library()&lt;br /&gt;
&lt;br /&gt;
@register.filter&lt;br /&gt;
def exp(value, arg):&lt;br /&gt;
if value.isdigit() and arg.isdigit():&lt;br /&gt;
return int(value)**int(arg)&lt;br /&gt;
else:&lt;br /&gt;
return '&amp;lt;span style=“color:red”&amp;gt;Error&amp;lt;/span&amp;gt;'&lt;br /&gt;
exp.is_safe = True&amp;lt;/source &amp;gt;&lt;br /&gt;
Мы создали фильтр exp, который получает значение и показатель степени и возводит одно в другое; если аргументы не являются числами, генерируется ошибка. В строке 5 мы регистрируем&lt;br /&gt;
фильтр в системе с помощью декоратора. Строка 11 указывает,&lt;br /&gt;
что exp может возвращать HTML-код. Поскольку (в целях безопасности) он автоматически экранируется (&amp;lt; и &amp;gt; заменяются на &amp;amp;amp;lt; и&lt;br /&gt;
&amp;amp;amp;gt; и т.д.), то, желая видеть чистый HTML, мы должны запретить&lt;br /&gt;
такое поведение вручную. Следующим шагом является подгрузка&lt;br /&gt;
библиотеки фильтров в шаблон, для чего нужно добавить в него&lt;br /&gt;
следующую строку:&lt;br /&gt;
 {% load filters %}&lt;br /&gt;
Вообще-то Django ищет созданные библиотеки шаблонов в корне приложения, поэтому наш фильтр пока не будет доступен. Это&lt;br /&gt;
не очень удобно, особенно если мы хотим использовать один и тот&lt;br /&gt;
же фильтр во многих приложениях. Решение – создать для проекта&lt;br /&gt;
единую библиотеку, а в приложения помещать лишь символьные&lt;br /&gt;
ссылки на нее.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;ln -s /var/www/myproject/templatetags/ /var/www/myproject/news/&amp;lt;/source&amp;gt;&lt;br /&gt;
Теперь проверим работу фильтра, добавив в какой-нибудь шаблон&lt;br /&gt;
строку.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{ &amp;quot;4&amp;quot;|exp:&amp;quot;4&amp;quot; }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Во время компиляции она будет заменена на 256. Если же мы&lt;br /&gt;
напишем&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{ &amp;quot;a&amp;quot;|exp:&amp;quot;4&amp;quot; }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
то увидим слово «Error», выделенное красным цветом.&lt;br /&gt;
Кстати, если бы мы не указали в строке 11 фильтра exp.is_safe= True, можно было бы просто применить фильтр safe прямо в&lt;br /&gt;
шаблоне:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;{{ &amp;quot;a&amp;quot;|exp:&amp;quot;4&amp;quot;|safe }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
После регистрации фильтра в системе, информация о нем становится доступной по адресу http://127.0.0.1:8000/admin/doc/filters/&lt;br /&gt;
(рис. 4)&lt;br /&gt;
[[Изображение:LXF108 87 1.png|frame|center|Рис. 4. Система любезно подскажет, как использовать созданный вами фильтр.]]&lt;br /&gt;
&lt;br /&gt;
=== Компоненты ===&lt;br /&gt;
Если нам надо выполнить какие-либо действия до или после того,&lt;br /&gt;
как будет вызвано представление, либо в случае ошибки, можно создать свой компонент (middleware) или воспользоваться&lt;br /&gt;
поставляемым с Django. Мы уже делали это, когда изучали кэширование ([[LXF107:Django|LXF107]]). Напомню, что в файле settings.py есть кортеж&lt;br /&gt;
MIDDLEWARE_CLASSES, который перечисляет компоненты, задействованные в проекте. У нас это:&lt;br /&gt;
* django.middleware.common.CommonMiddleware Решает общие задачи: нормализует URL (добавляет префикс www и завершающий /), запрещает доступ к сайту определенным роботам, взаимодействует с Etag.&lt;br /&gt;
* django.contrib.sessions.middleware.SessionMiddleware Это сессии.&lt;br /&gt;
* django.contrib.auth.middleware.AuthenticationMiddleware А это – авторизация.&lt;br /&gt;
* django.middleware.doc.XViewMiddleware Используется для автоматического документирования Django.&lt;br /&gt;
* django.middleware.locale.LocaleMiddleware Интернационализация.&lt;br /&gt;
Помимо перечисленных выше, в Django доступны следующие&lt;br /&gt;
компоненты (django.middleware.*):&lt;br /&gt;
* gzip.GZipMiddleware Сжатие отправляемой страницы для экономии трафика.&lt;br /&gt;
* http.ConditionalGetMiddleware Поддержка условных GET-запросов для работы с Last-Modified и Etag.&lt;br /&gt;
* http.SetRemoteAddrFromForwardedFor Обратное проксирование.&lt;br /&gt;
* cache.CacheMiddleware Тот самый кэш, с которым мы сталкивались на прошлом уроке.&lt;br /&gt;
* transaction.TransactionMiddleware Компонент для включения в SQL-запросы транзакционных конструкций: COMMIT, ROLLBACK.&lt;br /&gt;
Заметьте, что не все базы данных поддерживают транзакции.&lt;br /&gt;
&lt;br /&gt;
И, наконец, django.contrib.csrf.middleware.CsrfMiddleware, защищающее от CSRF-атак.&lt;br /&gt;
&lt;br /&gt;
По сложившейся уже традиции, рассмотрим, как написать свой&lt;br /&gt;
собственный компонент. С точки зрения программиста, это просто&lt;br /&gt;
класс Python, имеющий ряд методов, вызываемых Django в определенные моменты времени. Первым из них является конструктор&lt;br /&gt;
__init__(self), регистрирующий компонент в системе. Дальше следуют методы, определяющие порядок выполнения кода:&lt;br /&gt;
* process_request() – запускается после того, как поступил запрос,но перед тем как Django начнет искать запрашиваемый адрес в URL-картах;&lt;br /&gt;
* process_view() – отрабатывает, когда конкретное представление уже определено, но еще не запущено;&lt;br /&gt;
* process_response() – выполняется после представления.&lt;br /&gt;
Используется для сжатия сгенерированного HTML.&lt;br /&gt;
process_exception() – вызывается, если что-то пошло не так или&lt;br /&gt;
было возбуждено необработанное исключение.&lt;br /&gt;
&lt;br /&gt;
Вот, в сущности, и все. Впрочем, нет – взгляните на врезку И&lt;br /&gt;
прочая, прочая, прочая, почитайте документацию или свободную&lt;br /&gt;
книгу о Django – Django Book (http://www.djangobook.com); если же вам&lt;br /&gt;
больше по душе русский, советую заглянуть на http://cargo.caml.ru/djangobook. Наконец, примените полученные знания на практике – и дайте нам знать, если у вас получится что-то действительно стоящее!&lt;br /&gt;
&lt;br /&gt;
{{Врезка|center|&lt;br /&gt;
|Заголовок=И прочая, прочая, прочая...&lt;br /&gt;
|Содержание=За четыре урока мы успели рассмотреть почти все возможности&lt;br /&gt;
Django, но кое-что осталось неохваченным...&lt;br /&gt;
*; Электронная почта&lt;br /&gt;
Django предлагает высокоуровневый API для отправки письма в&lt;br /&gt;
одно действие:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;from django.core.mail import send_mail&lt;br /&gt;
send_mail('Тема', 'сообщение.', 'from@example.com', ['to@example.com'], fail_silently=False)&amp;lt;/source&amp;gt;&lt;br /&gt;
Кроме того, есть функции массовой рассылки сообщений, оповещения администраторов и менеджеров сайта, а также работы с&lt;br /&gt;
различным содержимым (HTML, текст, графика и т.п.)&lt;br /&gt;
*; CSV и PDF&lt;br /&gt;
Django позволяет легко формировать файлы с данными, разделенными запятыми (CSV), а также PDF-документы, используя&lt;br /&gt;
библиотеку ReportLab (http://www.reportlab.org/rl_toolkit.html).&lt;br /&gt;
*; Постраничный вывод&lt;br /&gt;
Когда количество объектов настолько велико, что одной страницы становится мало, на помощь приходит специальный класс&lt;br /&gt;
Paginator, который помогает организовать постраничный вывод:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; from django.core.paginator import Paginator&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; objects = ['django', 'python', 'mysql', 'apache']&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; p = Paginator(objects, 2)&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; page1 = p.page(1)&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; page1.object_list&lt;br /&gt;
['django', 'python']&amp;lt;/source&amp;gt;&lt;br /&gt;
Поскольку большинство наших объектов хранятся в базе данных,&lt;br /&gt;
Django также предлагает класс QuerySetPaginator, который принимает не список, а множество объектов из СУБД.&lt;br /&gt;
*; Карты сайта&lt;br /&gt;
Хотите, чтобы ваш сайт правильно индексировался поисковыми&lt;br /&gt;
машинами? Необходимо создать его карту! Django поможет и&lt;br /&gt;
здесь, а функция django.contrib.sitemaps.ping_google «заставит»&lt;br /&gt;
Google обновить индексы для вашего сайта.&lt;br /&gt;
*; Управление несколькими сайтами&lt;br /&gt;
Одной из задач, с которой успешно справляется Django, является&lt;br /&gt;
управление несколькими схожими по тематике сайтами из одной&lt;br /&gt;
инсталляции. Проект первоначально разрабатывался как платформа для новостных порталов, и одна новость могла появиться&lt;br /&gt;
сразу на нескольких ресурсах.&lt;br /&gt;
*; В помощь дизайнерам&lt;br /&gt;
Подгрузив к шаблонам модуль помощи дизайнерам:&lt;br /&gt;
 {% load webdesign %}&lt;br /&gt;
вы получите в свое распоряжение тэг {% lorem %}, с помощью&lt;br /&gt;
которого можно выводить известную латинскую фразу «lorem&lt;br /&gt;
ipsum ...» для заполнения шаблонов «рыбой».&lt;br /&gt;
*; И более того&lt;br /&gt;
Django содержит много других «вкусностей», которые очень&lt;br /&gt;
хорошо описаны в прилагающейся документации на английском&lt;br /&gt;
языке.&lt;br /&gt;
|Ширина=}}&lt;/div&gt;</summary>
		<author><name>Shultais</name></author>	</entry>

	</feed>