Django提供了一个可选的“站点”框架。它是一个将对象和功能关联到特定网站的钩子,它是域名和Django支持的站点的“详细”名称的存放处。
如果您的单个Django安装为多个站点供电,并且需要以某种方式区分这些站点,请使用它。
站点框架主要基于此模型:
用于存储 domain
和 name
网站的属性。
与网站关联的完全限定域名。例如, www.example.com
.
网站的人类可读的“详细”名称。
这个 SITE_ID
设置指定的数据库ID Site
与该特定设置文件关联的对象。如果省略设置,则 get_current_site()
函数将尝试通过比较 domain
主机名来自 request.get_host()
方法。
如何使用它取决于您自己,但是Django通过一些约定自动使用它。
你为什么要使用网站?最好用例子来解释。
这个 LJWorld.com 和Lawrence.com网站是由同一新闻机构运营的--堪萨斯州劳伦斯的《劳伦斯日报-世界报》。LJWorld.com专注于新闻,而Lawrence.com专注于当地娱乐。但有时编辑们想发表一篇关于 both 网站。
解决这个问题的幼稚方法是要求网站制作者发布同一篇文章两次:一次为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
像那样硬编码站点ID是很脆弱的,以防它们发生变化。完成相同任务的更简单的方法是检查当前站点的域:
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,),
"editor@%s" % current_site.domain,
[user.email],
)
# ...
在lawrence.com上,此电子邮件的主题行为“感谢订阅lawrence.com alerts”。在ljworld.com上,此电子邮件的主题为“感谢订阅ljworld.com alerts”。电子邮件的邮件正文也是如此。
请注意,更灵活(但更重)的方法是使用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, "editor@ljworld.com", [user.email])
# ...
在这种情况下,您必须创建 subject.txt
和 message.txt
ljworld.com和lawrence.com模板目录的模板文件。这给了你更多的灵活性,但也更复杂。
利用 Site
尽可能多的对象,以消除不必要的复杂性和冗余。
姜戈的 get_absolute_url()
约定适用于获取不带域名的对象的URL,但在某些情况下,您可能希望显示完整的URL-- https://
以及域和所有东西--对于一个对象。为此,您可以使用Sites框架。举个例子:
>>> 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
寄存器A post_migrate
创建名为 example.com
随域 example.com
. 此站点也将在Django创建测试数据库之后创建。要为项目设置正确的名称和域,可以使用 data migration .
为了在生产中为不同的站点提供服务,您将创建一个单独的设置文件 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
¶如果 Site
在应用程序中扮演关键角色,请考虑使用 CurrentSiteManager
在您的模型中。这是一个模型 manager 自动筛选查询以仅包含与当前 Site
.
使用 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
或A ManyToManyField
调用 sites
过滤。如果使用的字段名不是 site
或 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
. 如中所述 manager documentation ,如果手动定义管理器,则Django不会创建自动 objects = models.Manager()
你的经理。还要注意,django的某些部分——即django管理站点和通用视图——使用定义的管理器 第一 在模型中,如果您希望您的管理站点能够访问所有对象(而不仅仅是特定于站点的对象),请将 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安装只为一个站点供电,您也应该花两秒钟的时间用您的 domain
和 name
,并在您的 SITE_ID
设置。
Django如何使用网站框架:
在 redirects framework
,每个重定向对象都与特定站点关联。当Django搜索重定向时,它会考虑到当前站点。
在 flatpages framework
,每个平面图都与特定站点关联。创建平面图时,可以指定其 Site
和 FlatpageFallbackMiddleware
在检索要显示的平面图时检查当前网站。
在 syndication framework
,模板 title
和 description
自动访问变量 {{{{ site }}}}
,这就是 Site
表示当前站点的对象。另外,用于提供项目URL的钩子将使用 domain
从现在开始 Site
如果未指定完全限定的域,则为。
在 authentication framework
, django.contrib.auth.views.LoginView
通过电流 Site
模板名称为 {{{{ site_name }}}}
.
快捷方式视图 (django.contrib.contenttypes.views.shortcut
)使用当前域 Site
计算对象的URL时为。
在管理框架中,“现场查看”链接使用当前 Site
为它将重定向到的站点计算域。
RequestSite
对象¶一些 django.contrib 应用程序利用了站点框架,但其架构却没有 要求 要安装在数据库中的站点框架。(有些人不想,或者只是不想 able 为了安装站点框架所需的额外数据库表。)对于这些情况,框架提供了 django.contrib.sites.requests.RequestSite
类,当数据库支持的站点框架不可用时,可以将其用作回退。
共享的主接口的类 Site
(也就是说,它有 domain
和 name
属性),但从Django获取数据 HttpRequest
对象,而不是来自数据库。
设置 name
和 domain
属性的值 get_host()
.
A RequestSite
对象具有与普通对象相似的接口 Site
对象,但其 __init__()
方法采用 HttpRequest
对象。它可以推断 domain
和 name
通过查看请求的域。它有 save()
和 delete()
匹配的接口的方法 Site
但是方法却提高了 NotImplementedError
.
get_current_site
捷径¶最后,为了避免重复的回退代码,框架提供了 django.contrib.sites.shortcuts.get_current_site()
功能。
检查是否 django.contrib.sites
安装并返回当前 Site
对象或 RequestSite
基于请求的对象。它根据以下内容查找当前网站 request.get_host()
如果 SITE_ID
未定义设置。
域和端口都可以由返回 request.get_host()
当主机头显式指定了端口时,例如 example.com:80
. 在这种情况下,如果由于主机与数据库中的记录不匹配而导致查找失败,则会删除端口,并仅使用域部分重试查找。这不适用于 RequestSite
它将始终使用未修改的主机。
12月 18, 2023