Система сайтов Django является базовой средой, которая позволяет вам управлять множеством сайтов с помощью одной базы данных и одного проекта Django. Это абстрактная концепция и её не всегда легко понять, поэтому мы начнём с описания ряда сценариев где эта особенность Django может быть полезной.
Как мы рассказывали в главе «Введение в Django», сайты LJWorld.com и Lawrence.com управляются одной новостной компанией: газетой Lawrence Journal-World в городе Lawrence, штат Канзас. Сайт LJWorld.com сфокусирован на новостях, в то время как сайта Lawrence.com сосредоточен на местных развлечениях. Но иногда корреспонденты желают разместить свою статью на обоих сайтах.
Мозгодробящим способом решения этой задачи будет использование раздельных баз данных для каждого сайта и наличие требования к корреспондентам о необходимости размещения их статьи дважды: на одном и на другом сайтах. Но этот метод неэффективен и приводит к дублированию информации в базе данных.
Есть ли решение получше? Оба сайта используют единую базу данных для статей и статья ассоциирована с одним или несколькими сайтами с помощью отношения «многие-ко-многим». Среда управления сайтами предоставляет таблицу базы данных через которую статьи ассоциируются с сайтами.
Сайты LJWorld.com и Lawrence.com имеют функциональность для уведомления пользователей по электронной почте о появившихся новостях. Это очень просто: пользователь регистрируется на форме сайта и немедленно получает по электронной почте сообщение: «Спасибо за вашу подписку.»
Будет неэффективно и избыточно реализовывать код регистрации дважды, таким образом сайты должны использовать единый код. Но сообщение должно различаться для каждого сайта. Используя объекты Site, мы можем абстрагировать ваше уведомление так, что оно будет использовать атрибуты объекта name (т.е., LJWorld.com) и domain (т.е., www.ljworld.com).
Среда управления сайтами предоставляет место для хранения атрибутов name и domain для каждого сайта вашего проекта Django и это означает, что вы можете повторно использовать эти значения в общем виде.
Среда управления сайтами больше походит на ряд удобств, чем на полноценную среду. Всё основано на двух базовых концепциях:
Модель Site, которая находится в модуле django.contrib.sites, имеет атрибуты name и domain.
Параметр SITE_ID определяет идентификатор в базе данных для объекта Site, который ассоциирован с определённым файлом настроек.
То, как будут использоваться эти две концепции, полностью зависит от вас, но Django автоматически использует их в ряде случаев.
Для того, чтобы установить приложение среды управления сайтами, выполните следующие шаги:
Добавьте значение django.contrib.sites в параметр INSTALLED_APPS.
Выполните команду manage.py syncdb для установки таблицы django_site в вашу базу данных.
Добавьте один или более объектов Site, через интерфейс администратора Django или через Python API. Создайте объект Site для каждого сайта/домена, который управляется с помощью проекта Django.
Определите параметр SITE_ID в каждом из ваших файлов конфигурации. Это значение должно быть идентификатором объекта Site в базе данных для сайта, которому принадлежит редактируемый файл конфигурации.
Следующие секции описывают различные варианты использования среды управления сайтами.
Для повторного использования данных на множестве сайтов, как было описано в «Сценарий 1: Использование данных на множестве сайтов», следует создать поле ManyToManyField в модели Site, например:
from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(max_length=200)
# ...
sites = models.ManyToManyField(Site)
Эта инфраструктура необходима вам для ассоциирования статей со множеством сайтов в вашей базе данных. Используя это, вы можете использовать один и тот же код функции представления для множества сайтов. Продолжая работу с примером модели Article, так может выглядеть код представления article_detail:
from django.conf import settings
def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID)
except Article.DoesNotExist:
raise Http404
# ...
Эта функция представления может быть использована повторно, потому что она динамически проверяет сайт для статьи, учитывая значения параметра SITE_ID.
Например, предположим что SITE_ID для LJWorld.com имеет значение 1, а для Lawrence.com — значение 2. Если данное представление будет вызвано с использованием конфигурационного файла от LJWorld.com, то выборка статей будет ограничена теми записями, у которых в списке сайтов есть LJWorld.com.
Аналогично, вы можете ассоциировать модель к Site как многие-к-одному с помощью ForeignKey.
Например, если отображение статьи разрешено только для одного сайта, вы можете использовать следующую модель:
from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(max_length=200)
# ...
site = models.ForeignKey(Site)
Такой подход имеет те же преимущества, аналогичные описанным в секции «Фильтры разметки».
На низком уровне, вы можете использовать среду управления сайтами в ваших представлениях для выполнения определённых действия, основываясь на текущем сайте, например:
from django.conf import settings
def my_view(request):
if settings.SITE_ID == 3:
# Делаем что-то.
else:
# Делаем что-то другое.
Конечно, руки бы оторвать за такой код с жёстким определением идентификаторов. Более явным способом выполнения тех же действий является проверка домена текущего сайта:
from django.conf import settings
from django.contrib.sites.models import Site
def my_view(request):
current_site = Site.objects.get(id=settings.SITE_ID)
if current_site.domain == 'foo.com':
# Делаем что-то.
else:
# Делаем что-то другое.
Идея получения объекта Site для определения
значения параметра конфигурации SITE_ID
довольно стандартна, поэтому менеджер модели
Site (Site.objects) имеет
метод get_current(). Данный пример
эквивалентен предыдущему:
from django.contrib.sites.models import Site
def my_view(request):
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Делаем что-то.
else:
# Делаем что-то другое.
Замечание
В последнем примере нет необходимости импортирования django.conf.settings.
При использовании методики «Не повторяйся» название вашего сайта и его доменное имя, как было показано в секции «Сценарий 2: Хранение информации о сайте в одном месте», являются всего лишь ссылками на атрибуты name и domain объекта Site. Например:
from django.contrib.sites.models import Site
from django.core.mail import send_mail
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
current_site = Site.objects.get_current()
send_mail('Thanks for subscribing to %s alerts' % current_site.name,
'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % current_site.name,
'editor@%s' % current_site.domain,
[user_email])
# ...
Продолжая наш пример с LJWorld.com и Lawrence.com, на последнем сообщение имеет заголовок «Thanks for subscribing to lawrence.com alerts.» На LJWorld.com, соответственно, такой — «Thanks for subscribing to LJWorld.com alerts.» Аналогичное поведение применяется и для тела сообщения.
Более гибким (но и более сложным) способом реализации такого поведения будет использование шаблонной системы Django. Предположим, что сайты LJWorld.com и Lawrence.com хранят свои шаблоны в разных каталогах (TEMPLATE_DIRS), тогда переложим ответственность на шаблонную систему:
from django.core.mail import send_mail
from django.template import loader, Context
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
subject = loader.get_template('alerts/subject.txt').render(Context({}))
message = loader.get_template('alerts/message.txt').render(Context({}))
send_mail(subject, message, 'do-not-reply@example.com', [user_email])
# ...
В данном случае вам потребуется создать шаблоны
subject.txt и
message.txt в обоих каталогах с
шаблонами. Как упоминалось ранее, это даст вам большую
гибкость, но и работы прибавится.
Хорошей идеей будет максимально возможное развитие объекта Site, чтобы удалить ненужную сложность и избыточность.
Метод get_absolute_url() удобен для
получения URL без доменного имени, но в некоторых случаях
может потребоваться отобразить полный URL. Для этого можно
использовать среду управления сайтами. Вот простой пример:
>>> from django.contrib.sites.models import Site
>>> obj = MyModel.objects.get(id=3)
>>> obj.get_absolute_url()
'/mymodel/objects/3/'
>>> Site.objects.get_current().domain
'example.com'
>>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'http://example.com/mymodel/objects/3/'
Если объект Site играет ключевую роль в вашем приложении, рассмотрите использование CurrentSiteManager в ваших моделях. Это менеджер модели, описанный в приложении «Справочник определений модели», который автоматически обрабатывает свои запросы так, чтобы они включали только объекты ассоциированные с текущим сайтом.
Для использование CurrentSiteManager его следует явно подключить в модель. Например:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(max_length=100)
pub_date = models.DateField()
site = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager()
При использовании этой модели, вызов Photo.objects.all() возвратит все объекты Photo из базы данных, а вызов Photo.on_site.all() — только те объекты Photo, которые относятся к текущему сайту (это определяется с помощью параметра SITE_ID).
Другими словами, эти два оператора эквивалентны:
Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()
Как же CurrentSiteManager определяет какое поле объекта Photo относится к Site? По умолчанию он смотрит на поле с именем site. Если ваша модель имеет поля ForeignKey или ManyToManyField с именами отличными от site, вам потребуется явно передать имя в виде параметра в CurrentSiteManager. Нижеприведённая модель, у которой такое поле называется publish_on, демонстрирует такой случай:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(max_length=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')
Если вы попытаетесь использовать CurrentSiteManager, передав имя несуществующего поля, Django вызовет исключение ValueError.
Замечание
Вероятно вы пожелаете иметь обычный (не привязанный к сайтам) Manager для ваших моделей, даже если вы используете CurrentSiteManager. Как описано в приложении «Справочник определений модели», если вы определите менеджер вручную, тогда Django не будет создавать менеджер автоматически objects = models.Manager().
Также определённые части Django, а именно интерфейс администратора и базовые представления, используют тот менеджер, который был определён первым в модели. Таким образом, если потребуется, чтобы ваш сайт администратора имел доступ ко всем объектам (а не только к тем, которые относятся к сайту), следует поместить objects = models.Manager() в модели до определения CurrentSiteManager.
Несмотря на то, что использование среды управления сайтами необязательно, это настоятельно рекомендуется делать, потому что Django пользуется некоторыми преимуществами среды. Даже если Django используется для управления единственным сайтом, вы должны потратить несколько секунд на создание объекта Site с правильными значениями атрибутов name и domain, а затем указать на этот объект с помощью параметра SITE_ID.
Опишем, как Django использует среду управления сайтами:
В среде управления перенаправлением (смотрите далее секцию «Перенаправления») каждый объект перенаправления ассоциирован с определённым сайтом. Когда Django производит поиск для перенаправления пользователя, оно принимает во внимание текущее значение SITE_ID.
В среде управления комментариями каждый комментарий ассоциирован с определённым сайтом. При размещении комментария, его атрибут site принимает значение текущего SITE_ID, а при отображении комментариев через соответствующий шаблонный тег, отображаются только те комментарии, которые имеют отношение к текущему сайту.
В системе управления статическими страницами (смотрите далее секцию «Статические страницы») каждая такая страница ассоциирована с определённым сайтом. При создании статической страницы вы указываете для неё атрибут site, а система управления проверяет текущий SITE_ID при её отображении.
В системе управления трансляциями (обратитесь к разделу «Средства трансляции») шаблоны для title и description автоматически получают доступ к переменной {{ site }}, которая является объектом Site. Также атрибут domain объекта Site используется обработчиком URL в случае, если вы не указали полностью определённым домен.
В системе аутентификации и авторизации (обратитесь к разделу «Аутентификация пользователей») представление django.contrib.auth.views.login передаёт текущее имя сайта в шаблон через {{ site_name }}.
| Пред. | Уровень выше | След. |
| Глава 14. Средства от других разработчиков | Начало | Статические страницы |
0 comments | Make a comment