Инфраструктура "сайтов" ¶
Django поставляется с дополнительной системой «сайтов». Это точка расширения для связывания объектов и функций с определенными веб-сайтами, а также место перегруппировки доменных имен и «значимых» имен ваших сайтов Django.
Используйте это, если одна установка Django используется более чем для одного веб-сайта, и вам нужно различать эти сайты по некоторым пунктам.
Инфраструктура сайта в основном основана на этой модели:
-
класс
models.
Site
¶ Шаблон для хранения атрибутов
domain
иname
сайта.-
domain
¶ Полное доменное имя, связанное с сайтом. Например,
www.example.com
.
-
name
¶ Читаемое "значащее" имя для веб-сайта.
-
Параметр SITE_ID
указывает идентификатор базы данных объекта, Site
связанного с этим конкретным файлом настроек. Если этот параметр отсутствует, функция get_current_site()
пытается получить текущий сайт, сравнивая его с domain
именем хоста из метода request.get_host()
.
Как вы его используете, зависит от вас, но Django автоматически использует его в ряде ситуаций, используя несколько соглашений.
Пример использования ¶
Зачем вам нужна система сайта? Примеры предлагают лучшее объяснение.
Связывание контента с несколькими разными сайтами ¶
В LJWorld.com и Lawrence.com сайты управляются одним и тем же агентством новостей - газеты Lawrence Journal-World в Лоуренс, штат Канзас. LJWorld.com сосредоточился на текущих событиях, а Lawrence.com - на местных развлечениях. Но иногда редакция хотела опубликовать статью на обоих сайтах.
Наивный способ решить эту проблему - потребовать от каждого редактора сайта дважды опубликовать одну и ту же историю: один раз для LJWorld.com и один раз для Lawrence.com. Но это действительно неэффективно для производителей сайтов, плюс излишне хранить несколько копий одной и той же истории в базе данных.
Лучшее решение позволяет избежать дублирования контента: оба сайта используют одну и ту же статью в базе данных, и каждая статья связана с одним или несколькими сайтами. В терминологии модели Django это представлено полем ManyToManyField
в модели Article
:
from django.contrib.sites.models import Site
from django.db import models
class Article(models.Model):
headline = models.CharField(max_length=200)
# ...
sites = models.ManyToManyField(Site)
Это элегантно делает несколько вещей:
Производители сайтов могут редактировать весь контент для обоих сайтов в едином интерфейсе (администрирование Django).
Один и тот же рассказ не нужно публиковать дважды в базе данных; это отдельная запись в базе данных.
Разработчики сайтов могут использовать один и тот же код представления Django для обоих сайтов. Код представления, отображающий данную историю, подтверждает, что запрошенная история действительно находится на текущем сайте. Это дает что-то вроде:
from django.contrib.sites.shortcuts import get_current_site def article_detail(request, article_id): try: a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id) except Article.DoesNotExist: raise Http404("Article does not exist on this site") # ...
Связывание контента с одним сайтом ¶
Точно так же вы можете связать модель с моделью Site
с помощью отношения «многие к одному» с ключом ForeignKey
.
Например, если статья будет появляться только на одном сайте, вы можете использовать такой шаблон:
from django.contrib.sites.models import Site
from django.db import models
class Article(models.Model):
headline = models.CharField(max_length=200)
# ...
site = models.ForeignKey(Site, on_delete=models.CASCADE)
Преимущества такие же, как и в предыдущем разделе.
Перехват текущего сайта в представлениях ¶
Вы можете использовать инфраструктуру сайта в представлениях Django для выполнения определенных операций в зависимости от сайта, для которого требуется представление. Например :
from django.conf import settings
def my_view(request):
if settings.SITE_ID == 3:
# Do something.
pass
else:
# Do something else.
pass
Жестко запрограммировать такие идентификаторы сайтов на случай их изменения хрупко. Более чистый способ сделать то же самое - проверить домен текущего сайта:
from django.contrib.sites.shortcuts import get_current_site
def my_view(request):
current_site = get_current_site(request)
if current_site.domain == 'foo.com':
# Do something
pass
else:
# Do something else.
pass
Это также имеет то преимущество, что проверяет, установлена ли инфраструктура сайтов, и возвращает экземпляр, RequestSite
если это не так.
Если у вас нет доступа к объекту запроса, вы можете использовать метод get_current()
диспетчера моделей Site
. Затем вы должны убедиться, что файл настроек содержит настройку SITE_ID
. Этот пример эквивалентен предыдущему:
from django.contrib.sites.models import Site
def my_function_without_request():
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Do something
pass
else:
# Do something else.
pass
Доступ к текущему домену для просмотра ¶
LJWorld.com и Lawrence.com имеют функцию оповещения по электронной почте, которая позволяет читателям подписаться на получение уведомлений при появлении новостей. Это довольно просто: читатель регистрируется с помощью веб-формы и сразу получает электронное письмо со словами «Спасибо за регистрацию».
Было бы неэффективно и излишне реализовывать код для этого процесса регистрации дважды, в результате чего сайты использовали бы один и тот же код в фоновом режиме. Но сообщение «спасибо за регистрацию» должно быть разным для каждого сайта. Используя объекты Site
, можно абстрагироваться от сообщения «спасибо», чтобы использовать значения name
и, domain
соответствующие текущему сайту.
Вот пример, который может составлять управляющий код формы в представлении:
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
current_site = get_current_site(request)
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,
),
'[email protected]%s' % current_site.domain,
[user.email],
)
# ...
Для Lawrence.com тема письма - «Спасибо за подписку на уведомления lawrence.com. ». Тема письма LJWorld.com - «Спасибо за подписку на уведомления LJWorld.com. ». То же самое и с телами писем.
Обратите внимание, что еще более гибкий (но громоздкий) способ сделать это - использовать систему шаблонов Django. Предполагая, что сайты Lawrence.com и LJWorld.com имеют разные каталоги шаблонов ( DIRS
), достаточно оставить настройку сообщений системе шаблонов, например:
from django.core.mail import send_mail
from django.template import loader
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
subject = loader.get_template('alerts/subject.txt').render({})
message = loader.get_template('alerts/message.txt').render({})
send_mail(subject, message, '[email protected]', [user.email])
# ...
В этом случае по-прежнему необходимо будет создать файлы шаблонов subject.txt
и message.txt
в двух каталогах шаблонов LJWorld.com и Lawrence.com. Это обеспечивает максимальную гибкость, но также является более сложным.
Рекомендуется максимально использовать объекты, Site
чтобы устранить ненужную сложность и избыточность.
Доступ к текущему домену для полных URL ¶
Соглашение get_absolute_url()
Django полезно для получения URL-адресов объектов без имени домена, но в некоторых случаях может быть желательно отобразить полный URL-адрес объекта с http://
доменом и. Для этого можно использовать инфраструктуру сайта. Пример
>>> 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'
>>> 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'https://example.com/mymodel/objects/3/'
Активация инфраструктуры сайта ¶
Чтобы активировать инфраструктуру сайта, выполните следующие действия:
Добавьте
'django.contrib.sites'
к своей настройкеINSTALLED_APPS
.Определите настройку
SITE_ID
:SITE_ID = 1
Беги
migrate
.
django.contrib.sites
регистрирует обработчик сигнала, post_migrate
который создает сайт по умолчанию example.com
с именем домена example.com
. Этот сайт также создан после создания тестовой базы данных Django. Чтобы установить правильное имя и домен для вашего проекта, вы можете использовать миграцию данных .
Чтобы обслуживать разные сайты в производственной среде, необходимо создать отдельный файл настроек для каждого из них с SITE_ID
определенным (возможно, импорт из общего файла настроек, чтобы избежать дублирования общих настроек), а затем определить стоимостьDJANGO_SETTINGS_MODULE
подходит для каждого сайта.
Кеширование Site
текущего объекта ¶
Поскольку текущий сайт хранится в базе данных, каждый вызов Site.objects.get_current()
может привести к запросу базы данных. Но Django немного умнее этого: при первом запросе текущий сайт кэшируется, и любой последующий вызов этого метода возвращает кэшированные данные, а не обращается к базе данных.
Если по какой-то причине вы хотите принудительно выполнить новый запрос к базе данных, вы можете указать Django очистить кэшированный контент, используя Site.objects.clear_cache()
:
# First call; current site fetched from database.
current_site = Site.objects.get_current()
# ...
# Second call; current site fetched from cache.
current_site = Site.objects.get_current()
# ...
# Force a database query for the third call.
Site.objects.clear_cache()
current_site = Site.objects.get_current()
Менеджер CurrentSiteManager
¶
-
класс
managers.
CurrentSiteManager
¶
Если он Site
играет важную роль в вашем приложении, рассмотрите возможность использования менеджера CurrentSiteManager
в ваших моделях. Это менеджер моделей, который автоматически фильтрует свои запросы, чтобы включать только объекты, связанные с Site
текущим.
SITE_ID
обязательное
CurrentSiteManager
можно использовать только в том случае, если этот параметр SITE_ID
определен в ваших настройках.
Используйте CurrentSiteManager
, явно добавляя его в свои модели. Например :
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models
class Photo(models.Model):
photo = models.FileField(upload_to='photos')
photographer_name = models.CharField(max_length=100)
pub_date = models.DateField()
site = models.ForeignKey(Site, on_delete=models.CASCADE)
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
? По умолчанию CurrentSiteManager
выполняется фильтрация либо по ForeignKey
именованному ключу, site
либо по ManyToManyField
именованному полю sites
. Если вы используете поле с именем, отличным от `` сайта '', или sites
для обозначения объектов, с Site
которыми связаны ваши объекты, необходимо явно передать имя настраиваемого поля в качестве параметра CurrentSiteManager
в шаблоне. Следующая модель с именованным полем publish_on
демонстрирует это:
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models
class Photo(models.Model):
photo = models.FileField(upload_to='photos')
photographer_name = models.CharField(max_length=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')
Если вы попытаетесь использовать CurrentSiteManager
и дать ему несуществующее имя поля, Django выдаст исключение ValueError
.
Наконец, обратите внимание, что рекомендуется сохранить Manager
обычный (без ссылок на сайты) для ваших моделей, даже если вы используете CurrentSiteManager
. Как объясняется в документации обработчика , если вы вручную определяете обработчик, Django не создает автоматический обработчик для вас. Также имейте в виду, что некоторые части Django, особенно сайт администратора и общие представления, используют первый обработчик, определенный для модели, поэтому, если вы хотите, чтобы сайт администратора имел доступ ко всем объектам (а не только к тем, что в 'конкретный сайт), добавьте в свои шаблоны перед определением .objects = models.Manager()
objects = models.Manager()
CurrentSiteManager
Промежуточное ПО сайта ¶
Если вы часто используете этот паттерн:
from django.contrib.sites.models import Site
def my_view(request):
site = Site.objects.get_current()
...
Чтобы избежать повторов, добавляйте django.contrib.sites.middleware.CurrentSiteMiddleware
к MIDDLEWARE
. Это промежуточное ПО устанавливает атрибут site
для каждого объекта запроса, который позволяет вызову request.site
получить доступ к текущему сайту.
Использование инфраструктуры сайта от Django ¶
Хотя использование инфраструктуры сайта не является обязательным, это настоятельно рекомендуется, поскольку Django использует ее в нескольких местах. Даже если ваша установка Django используется только одним сайтом, уделите несколько секунд, чтобы определить объект сайта с правильными значениями, domain
а name
затем укажите в настройке SITE_ID
идентификатор этого объекта.
Вот как Django использует инфраструктуру сайта:
- В «» каждый объект перенаправления связан с определенным сайтом. Когда Django ищет перенаправление, он учитывает текущий сайт.
application de redirection
- В «» каждая статическая страница связана с определенным сайтом. Когда создается статическая страница, вы должны указать ее , и промежуточное ПО проверяет текущий сайт, когда находит статические страницы для отображения.
application des pages statiques
Site
FlatpageFallbackMiddleware
- В l » шаблоны для и автоматически получают доступ к переменной , которая является объектом, представляющим текущий сайт. Аналогично, точка входа для предоставления URL-адресов элементов использует атрибут текущего объекта, если вы не укажете полностью определенный домен.
infrastructure de syndication
title
description
{{ site }}
Site
domain
Site
- В » представление передает имя текущего объекта в шаблон как .
infrastructure d'authentification
django.contrib.auth.views.LoginView
Site
{{ site_name }}
- Ярлык view (
django.contrib.contenttypes.views.shortcut
) использует доменSite
текущего объекта при построении URL-адреса объекта. - На сайте администрирования ссылка «посмотреть на сайте» использует
Site
текущий объект для определения домена сайта, на который происходит перенаправление.
Объекты RequestSite
¶
Некоторые приложения django.contrib полагаются на инфраструктуру сайтов, но не зависят от установки приложения сайтов в базе данных. (Некоторые люди не хотят или просто не имеют возможности устанавливать дополнительную таблицу базы данных, которая требуется инфраструктуре сайта.) Для таких ситуаций приложение предоставляет класс, django.contrib.sites.requests.RequestSite
который можно использовать в качестве решения для откат, когда дополнение базы данных инфраструктуры сайта недоступно.
-
класс
requests.
RequestSite
¶ Класс, который разделяет основной интерфейс
Site
(то есть имеет атрибутыdomain
иname
), но получает свои данные от объектаHttpRequest
в Django, а не из базы данных.-
__init__
( запрос ) ¶ Устанавливает атрибуты
name
и вdomain
соответствии со значениями, полученными изget_host()
.
-
Объект RequestSite
имеет интерфейс, подобный Site
обычному объекту , за исключением того, что его метод __init__()
принимает объект HttpRequest
. Он может определить домен и имя из него, исследуя домен запроса. У него есть методы save()
и delete()
для зеркалирования интерфейса Site
, но эти методы генерируют исключение NotImplementedError
.
Ярлык get_current_site
¶
Наконец, чтобы избежать дублирования кода, в системе предусмотрена функция django.contrib.sites.shortcuts.get_current_site()
.
-
shortcuts.
get_current_site
( запрос ) ¶ Функция, которая проверяет,
django.contrib.sites
установлена ли она, и возвращает либо текущий объект,Site
либо объект,RequestSite
определенный в запросе. Он определяет текущий сайт в зависимости отrequest.get_host()
того,SITE_ID
не задан ли параметр .request.get_host()
может возвращать и домен, и порт, например, если заголовокHost
содержит явный номер портаexample.com:80
. В таких случаях, если поиск завершается неудачно из-за того, что хост не соответствует записи в базе данных, порт удаляется и поиск возобновляется только с доменной частью. Это не относится к тем,RequestSite
кто все еще использует немодифицированный хост.