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

Django_-_podrobnoe_rukovodstvo

.pdf
Скачиваний:
373
Добавлен:
01.03.2016
Размер:
5 Мб
Скачать
☆

360

Глава 17. Дополнительные процессоры

ку. Таким образом, foo.com/bar переадресуется на foo.com/bar/, но foo. com/bar/file.txt не будет модифицирован.

Если параметр PREPEND_WWW содержит значение True, то URL, начинающийся не с «www.», переадресуется на такой же URL, но начинающийся с «www.»

Оба варианта предназначены для нормализации URL. Идея в том, что любому ресурсу должен соответствовать один и только один URL. С технической точки зрения URL-адреса example.com/bar, example.com/ bar/ и www.example.com/bar/ считаются различными. Поисковая сис­ тема будет считать их различными, что приведет к снижению рейтинга вашего сайта, поэтому эти URL лучше нормализовать.

•• Обрабатывает заголовки ETag в соответствии со значением параметра USE_ETAGS. Механизм ETag задуман как оптимизация условного кэширования страниц на уровне HTTP. Если параметр USE_ETAGS имеет значение True, то для каждого запроса Django будет вычислять значение заголовка ETag как MD5-свертку содержимого страницы и при необходимости отправлять клиенту ответ с заголовком

Not Modified.

Отметим, что существует также дополнительный процессор для условных GET-запросов (см. ниже), который, помимо прочего, обрабатывает заголовки ETag.

Процессор сжатия

Класс процессора: django.middleware.gzip.GZipMiddleware.

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

Обычно мы отдаем предпочтение высокому быстродействию, а не экономии трафика, но, если у вас другое мнение, можете включить этот процессор.

Процессор условных GET-запросов

Класс процессора: django.middleware.http.ConditionalGetMiddleware.

Поддерживает условные GET-запросы. Если в ответе имеется заголовок Last-Modified или ETag и при этом в запросе имеется заголовок If-None- Match или If-Modified-Since, то сгенерированный ответ заменяется ответом с кодом 304 («Не изменился»). Поддержка заголовка ETag зависит от значения параметра USE_ETAGS, при этом ожидается, что заголовок ETag уже включен в ответ. Как отмечалось выше, этот заголовок устанавливается процессором CommonMiddleware.

Встроенные дополнительные процессоры

361

Этот же процессор удаляет все содержимое из ответов на запросы HEAD и устанавливает во всех ответах заголовки Date и Content-Length.

Поддержка реверсивного прокси-сервера (дополнительный процессор X-Forwarded-For)

Класс процессора: django.middleware.http.SetRemoteAddrFromForwardedFor.

Этот процессор рассматривался в разделе «Что такое дополнительный процессор?» выше. Он устанавливает значение ключа request. META[‘REMOTE_ADDR’], исходя из значения ключа request.META[‘HTTP_X_ FORWARDED_FOR’], если последний присутствует. Это полезно, когда вебсервер находится за реверсивным прокси-сервером, в результате чего во всех запросах REMOTE_ADDR оказывается равным 127.0.0.1.

Внимание

Этот модуль не проверяет заголовок HTTP_X_FORWARDED_FOR.

Если ваш сервер не находится за реверсивным прокси-сервером, который автоматически устанавливает заголовок HTTP_X_FORWARDED_FOR, не пользуйтесь этим процессором. Любой человек может вставить подложный заголовок HTTP_X_FORWARDED_FOR, а поскольку от него зависит значение элемента REMOTE_ADDR , это означает, что будет подделан также IP-адрес.

Применяйте этот процессор только тогда, когда полностью доверяете значениям в заголовке HTTP_X_FORWARDED_FOR.

Процессор поддержки сеансов

Класс процессора: django.contrib.sessions.middleware.SessionMiddleware. Обеспечивает поддержку сеансов. Подробнее см. главу 14.

Процессор кэширования сайта

Классы процессора: django.middleware.cache.UpdateCacheMiddleware и django. middleware.cache.FetchFromCacheMiddleware.

Эти классы работают совместно и обеспечивают кэширование всех генерируемых Django страниц. Подробно обсуждалось в главе 15.

Процессор транзакций

Класс процессора: django.middleware.transaction.TransactionMiddleware.

Обеспечивает выполнение команды COMMIT или ROLLBACK в базе данных на этапе обработки запроса и формирования ответа. Если функция представления завершается нормально, выполняется COMMIT, а если возбуждает исключение, то ROLLBACK.

Местоположение этого процессора в списке имеет важное значение. Процессоры, выполняющиеся до него, следуют стандартной семантике

362

Глава 17. Дополнительные процессоры

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

Дополнительные сведения о транзакциях базы данных см. в приложении B.

Что дальше?

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

18

Интеграция с унаследованными базами данных и приложениями

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

Интеграция с унаследованной базой данных

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

Использование inspectdb

Утилита inspectdb просматривает базу данных, на которую указывает файл параметров, определяет для каждой таблицы структуру модели Django и выводит программный код модели на языке Python на стандартный вывод.

Ниже описана типичная пошаговая процедура интеграции с унаследованной базой данных. Предполагается лишь, что Django установлен и унаследованная база данных существует.

1.Создайте проект Django командой django-admin.py startproject mysite

(где mysite – имя проекта).

2.Откройте в редакторе файл параметров mysite/settings.py и определите параметры соединения с базой данных, в том числе ее имя. Точ-

364

Глава 18. Интеграция с унаследованными базами данных и приложениями

нее, требуется определить параметры DATABASE_NAME, DATABASE_ENGINE,

DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST и DATABASE_PORT. (Некоторые из них необязательны, подробности см. в главе 5.)

3.Создайте в проекте приложение Django, выполнив команду python mysite/manage.py startapp myapp (где myapp – имя приложения).

4.Выполните команду python mysite/manage.py inspectdb. Она проанализирует таблицы в базе данных DATABASE_NAME и для каждой из них выведет сгенерированный класс модели. Изучите этот класс, чтобы понять, что умеет inspectdb.

5.Сохраните сгенерированный код в файле models.py в каталоге приложения с помощью стандартного механизма перенаправления:

python mysite/manage.py inspectdb > mysite/myapp/models.py

6.Отредактируйте файл mysite/myapp/models.py, внеся в модель необходимые на ваш взгляд изменения. Некоторые идеи по этому поводу приведены в следующем разделе.

Правка сгенерированных моделей

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

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

•• В каждой сгенерированной модели все поля без исключения представлены атрибутами, в том числе и поля первичного ключа. Напомним, однако, что Django автоматически добавляет поле первичного ключа с именем id, если в таблице нет первичного ключа. Поэтому следует удалить строки такого вида:

id = models.IntegerField(primary_key=True)

Эти строки не просто лишние, но приведут к ошибке при добавлении новых записей в таблицы.

•• Тип поля (например, CharField, DateField) определяется по типу столбца таблицы (например, VARCHAR, DATE). Если inspectdb не знает, как отобразить тип столбца на тип поля модели, то выбирает тип TextField и оставляет в коде комментарий ‘This field type is a guess’ (Тип поля предположительный) рядом с сомнительным полем. Найдите все такие комментарии и при необходимости исправьте тип поля.

Интеграция с системой­ аутентификации

365

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

•• Если имя столбца базы данных является зарезервированным словом в языке Python (например, pass, class или for), то inspectdb добавит к имени соответствующего атрибута суффикс _field, а в атрибут db_column запишет истинное имя столбца.

Например, если в таблице имеется столбец типа INT с именем for, то в сгенерированной модели это поле будет определено так:

for_field = models.IntegerField(db_column=’for’)

Рядом с таким полем inspectdb оставит комментарий ‘Field renamed because it was a Python reserved word’ (Поле переименовано, так как это зарезервированное слово Python).

•• Если в базе данных имеются таблицы, ссылающиеся на другие таблицы (как чаще всего и бывает), то, возможно, придется изменить порядок следования сгенерированных моделей. Например, если в модели Book имеется поле типа ForeignKey, ссылающееся на модель Author, то модель Author должна предшествовать модели Book. Если необходимо создать связь с моделью, которая еще не была определена, то вместо самого объекта модели можно указать строку, содержащую ее имя.

•• Команда inspectdb благополучно распознает первичные ключи в таблицах баз данных PostgreSQL, MySQL и SQLite, то есть при необходимости она добавляет в определения полей атрибут primary_key=True. Для других СУБД вы сами должны добавить этот атрибут хотя бы для одного поля, поскольку Django требует, чтобы в любой модели присутствовало поле с атрибутом primary_key=True.

•• Команда inspectdb благополучно распознает внешние ключи в таблицах базы данных PostgreSQL и в некоторых типах таблиц MySQL. В остальных случаях поля внешнего ключа будут иметь в модели тип IntegerField в предположении, что столбец внешнего ключа имеет тип INT.

Интеграция с системой­ аутентификации

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

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

366

Глава 18. Интеграция с унаследованными базами данных и приложениями

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

Определение источников аутентификации

Django поддерживает специальный список источников информации для аутентификации. Когда вызывается метод django.contrib.auth. authenticate() (см. главу 14), фреймворк Django пытается выполнить аутентификацию пользователя, обращаясь ко всем источникам по очереди. Если первый источник возвращает ошибку, Django пробует второй и так далее до тех пор, пока не будут проверены все источники.

Список источников аутентификации определяется в параметре AUTHEN­ TICATION_BACKENDS. Это должен быть кортеж, состоящий из путей Python, указывающих на классы, которые реализуют процедуру аутентификации. Сами классы могут находиться в любом каталоге, указанном в пути Python.

По умолчанию параметр AUTHENTICATION_BACKENDS имеет следующее значение:

(‘django.contrib.auth.backends.ModelBackend’,)

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

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

Реализация собственного источника аутентификации

Источник аутентификации представляет собой класс, в котором реализованы два метода: get_user(id) и authenticate(**credentials).

Метод get_user принимает параметр id, который может быть именем пользователя, его идентификатором в базе данных и вообще чем угодно, и возвращает объект User.

Метод authenticate принимает учетные данные пользователя в виде набора именованных аргументов. Обычно он выглядит следующим образом:

class MyBackend(object):

def authenticate(self, username=None, password=None):

# Проверить имя пользователя и пароль и вернуть User.

Но можно также аутентифицировать по произвольному маркеру, например:

Интеграция с системой­ аутентификации

367

class MyBackend(object):

def authenticate(self, token=None):

#Проверить маркер и вернуть User.

Влюбом случае метод authenticate должен проверить полученные учетные данные и вернуть соответствующий объект User, если аутентификация прошла успешно. В противном случае он должен вернуть значение None.

Административный интерфейс Django тесно увязан с объектом User, который Django создает на основе информации, хранящейся в базе данных. Справиться с этой проблемой проще всего, создав объект User для каждого пользователя, зарегистрированного в источнике аутентификации (LDAP-каталоге, внешней базе данных и т. д.). Для этого можно написать сценарий, который сделает это заранее, или реализовать метод authenticate так, чтобы он создавал запись в базе данных Django при первом входе пользователя.

В следующем примере приводится реализация источника, который аутентифицирует пользователя, сравнивая его имя и пароль с параметрами в файле settings.py, и при первой успешной аутентификации создает объект Django User.

from django.conf import settings

from django.contrib.auth.models import User, check_password

class SettingsBackend(object): “””

Аутентифицировать, сравнивая с ADMIN_LOGIN и ADMIN_PASSWORD.

Использовать имя пользователя и свертку пароля, например:

ADMIN_LOGIN = ‘admin’

ADMIN_PASSWORD = ‘sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de’ “””

def authenticate(self, username=None, password=None): login_valid = (settings.ADMIN_LOGIN == username)

pwd_valid = check_password(password, settings.ADMIN_PASSWORD) if login_valid and pwd_valid:

try:

user = User.objects.get(username=username) except User.DoesNotExist:

#Создать нового пользователя. Пароль может быть

#любым, потому что он все равно не проверяется;

#вместо него проверяется пароль из settings.py. user = User(username=username,

password=’get from settings.py’) user.is_staff = True

user.is_superuser = True user.save()

368

Глава 18. Интеграция с унаследованными базами данных и приложениями

return user return None

def get_user(self, user_id): try:

return User.objects.get(pk=user_id) except User.DoesNotExist:

return None

Дополнительные сведения об источниках аутентификации см. в официальной документации по Django.

Интеграция с унаследованными веб приложениями

Приложение Django может работать на одном веб-сервере с приложением, реализованным на основе другой технологии. Самый простой способ добиться этого – определить в конфигурационном файле Apache, httpd.conf, образцы URL для разных технологий. (В главе 12 обсуждается развертывание Django на платформе Apache + mod_python, поэтому имеет смысл прочитать ее, прежде чем приступать к такого рода интеграции.)

Смысл в том, что тот или иной адрес URL передается Django для обработки, только если так определено в файле httpd.conf. По умолчанию (см. главу 12) предполагается, что Django должен обслуживать все страницы в определенном домене:

<Location “/”>

SetHandler python-program

PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings PythonDebug On

</Location>

Здесь директива <Location “/”> означает, что Django должен «обслуживать все URL, начиная от корня».

Однако ничто не мешает указать в директиве <Location> лишь некоторое поддерево каталогов. Пусть, например, имеется унаследованное приложение PHP, которое обслуживает большую часть страниц, а Django отводится роль административной части сайта /admin/, чтобы при этом не мешать работе PHP-кода. Для этого нужно указать в директиве

<Location> путь /admin/:

<Location “/admin/”> SetHandler python-program

PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings PythonDebug On

</Location>

Что дальше?

369

Теперь Django будет получать только запросы с адресами URL, начинающимися с /admin/, все остальные запросы будут обслуживаться так же, как и раньше.

Отметим, что такое связывание Django с квалифицированным URL (таким как /admin/) не влияет на алгоритм анализа запросов. Django всегда работает с абсолютными URL (например, /admin/people/person/add/), а не

с«урезанной» версией (/people/person/add/). Следовательно, образцы

вкорневой конфигурация URL должны начинаться с /admin/.

Что дальше?

Англоязычные читатели могли не обратить внимания на одну из замечательных особенностей административного интерфейса Django: он переведен на 50 с лишним языков! Это возможно благодаря подсис­ теме интернационализации Django (и упорному труду переводчиковдобровольцев). В следующей главе описывается, как с помощью этой системы­ можно создавать локализованные Django-сайты.