“Django/docs/3.0.x/ref/contrib/sites”的版本间差异

来自菜鸟教程
Django/docs/3.0.x/ref/contrib/sites
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:“站点”框架 — Django 文档}}
 
<div id="module-django.contrib.sites" class="section">
 
<div id="module-django.contrib.sites" class="section">
  
 
<span id="the-sites-framework"></span>
 
<span id="the-sites-framework"></span>
= The &quot;sites&quot; framework =
+
= “站点”框架 =
  
Django comes with an optional &quot;sites&quot; framework. It's a hook for associating
+
Django 带有一个可选的“站点”框架。 它是一个用于将对象和功能与特定网站相关联的钩子,它是您的 Django 站点的域名和“详细”名称的存放处。
objects and functionality to particular websites, and it's a holding place for
 
the domain names and &quot;verbose&quot; names of your Django-powered sites.
 
  
Use it if your single Django installation powers more than one site and you
+
如果您的单个 Django 安装支持多个站点,并且您需要以某种方式区分这些站点,请使用它。
need to differentiate between those sites in some way.
 
  
The sites framework is mainly based on this model:
+
站点框架主要基于这个模型:
  
 
<dl>
 
<dl>
<dt>''class'' <code>models.</code><code>Site</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">models.</span></span><span class="sig-name descname"><span class="pre">Site</span></span></dt>
<dd><p>A model for storing the <code>domain</code> and <code>name</code> attributes of a website.</p>
+
<dd><p>用于存储网站的 <code>domain</code> <code>name</code> 属性的模型。</p>
 
<dl>
 
<dl>
<dt><code>domain</code></dt>
+
<dt><span class="sig-name descname"><span class="pre">domain</span></span></dt>
<dd><p>The fully qualified domain name associated with the website.
+
<dd><p>与网站关联的完全限定域名。 例如,<code>www.example.com</code></p></dd></dl>
For example, <code>www.example.com</code>.</p></dd></dl>
 
  
 
<dl>
 
<dl>
<dt><code>name</code></dt>
+
<dt><span class="sig-name descname"><span class="pre">name</span></span></dt>
<dd><p>A human-readable &quot;verbose&quot; name for the website.</p></dd></dl>
+
<dd><p>网站的人类可读的“详细”名称。</p></dd></dl>
 
</dd></dl>
 
</dd></dl>
  
The [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting specifies the database ID of the
+
[[#id1|:setting:`SITE_ID`]] 设置指定与该特定设置文件关联的 [[#django.contrib.sites.models.Site|Site]] 对象的数据库 ID。 如果省略该设置,[[#django.contrib.sites.shortcuts.get_current_site|get_current_site()]] 函数将尝试通过将 [[#django.contrib.sites.models.Site.domain|domain]] [[../../request-response#django.http.HttpRequest|request.get_host()]] 中的主机名进行比较来获取当前站点] 方法。
[[#django.contrib.sites.models.Site|<code>Site</code>]] object associated with that
 
particular settings file. If the setting is omitted, the
 
[[#django.contrib.sites.shortcuts.get_current_site|<code>get_current_site()</code>]] function will
 
try to get the current site by comparing the
 
[[#django.contrib.sites.models.Site.domain|<code>domain</code>]] with the host name from
 
the [[../../request-response#django.http.HttpRequest|<code>request.get_host()</code>]] method.
 
  
How you use this is up to you, but Django uses it in a couple of ways
+
你如何使用它取决于你,但 Django 通过一些约定自动以几种方式使用它。
automatically via a couple of conventions.
 
  
 
<div id="example-usage" class="section">
 
<div id="example-usage" class="section">
  
== Example usage ==
+
== 示例用法 ==
  
Why would you use sites? It's best explained through examples.
+
你为什么要使用网站? 最好通过例子来解释。
  
 
<div id="associating-content-with-multiple-sites" class="section">
 
<div id="associating-content-with-multiple-sites" class="section">
  
=== Associating content with multiple sites ===
+
=== 将内容与多个站点相关联 ===
  
The [http://www.ljworld.com/ LJWorld.com] and [http://www.lawrence.com/ Lawrence.com] sites are operated by the same news
+
[http://www.ljworld.com/ LJWorld.com] [http://www.lawrence.com/ Lawrence.com] 网站由同一新闻机构运营 - 位于堪萨斯州劳伦斯的劳伦斯世界日报。 LJWorld.com 专注于新闻,而 Lawrence.com 专注于本地娱乐。 但有时编辑想在 '''' 站点上发表文章。
organization -- the Lawrence Journal-World newspaper in Lawrence, Kansas.
 
LJWorld.com focused on news, while Lawrence.com focused on local entertainment.
 
But sometimes editors wanted to publish an article on ''both'' sites.
 
  
The naive way of solving the problem would be to require site producers to
+
解决这个问题的天真的方法是要求网站制作人将同一个故事发布两次:一次是在 LJWorld.com,一次是在 Lawrence.com。 但这对网站制作者来说效率低下,而且在数据库中存储同一故事的多个副本是多余的。
publish the same story twice: once for LJWorld.com and again for Lawrence.com.
 
But that's inefficient for site producers, and it's redundant to store
 
multiple copies of the same story in the database.
 
  
A better solution removes the content duplication: Both sites use the same
+
一个更好的解决方案消除了内容重复:两个站点使用相同的文章数据库,并且一篇文章与一个或多个站点相关联。 在 Django 模型术语中,这由 <code>Article</code> 模型中的 [[../../models/fields#django.db.models|ManyToManyField]] 表示:
article database, and an article is associated with one or more sites. In
 
Django model terminology, that's represented by a
 
[[../../models/fields#django.db.models|<code>ManyToManyField</code>]] in the <code>Article</code> model:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第66行: 第47行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
 
from django.db import models
 
from django.db import models
  
第72行: 第53行:
 
     headline = models.CharField(max_length=200)
 
     headline = models.CharField(max_length=200)
 
     # ...
 
     # ...
     sites = models.ManyToManyField(Site)</pre>
+
     sites = models.ManyToManyField(Site)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This accomplishes several things quite nicely:
+
这很好地完成了几件事:
  
 
<ul>
 
<ul>
<li><p>It lets the site producers edit all content -- on both sites -- in a
+
<li><p>它允许站点制作者在单个界面(Django 管理员)中编辑所有内容——在两个站点上。</p></li>
single interface (the Django admin).</p></li>
+
<li><p>这意味着同一个故事不必在数据库中发布两次; 它在数据库中只有一条记录。</p></li>
<li><p>It means the same story doesn't have to be published twice in the
+
<li><p>它允许站点开发人员对两个站点使用相同的 Django 查看代码。 显示给定故事的视图代码会检查以确保请求的故事在当前站点上。 它看起来像这样:</p>
database; it only has a single record in the database.</p></li>
 
<li><p>It lets the site developers use the same Django view code for both sites.
 
The view code that displays a given story checks to make sure the requested
 
story is on the current site. It looks something like this:</p>
 
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.shortcuts import get_current_site
+
<syntaxhighlight lang="python">from django.contrib.sites.shortcuts import get_current_site
  
 
def article_detail(request, article_id):
 
def article_detail(request, article_id):
第97行: 第74行:
 
         a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id)
 
         a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id)
 
     except Article.DoesNotExist:
 
     except Article.DoesNotExist:
         raise Http404(&quot;Article does not exist on this site&quot;)
+
         raise Http404("Article does not exist on this site")
     # ...</pre>
+
     # ...</syntaxhighlight>
  
 
</div>
 
</div>
第108行: 第85行:
 
<div id="associating-content-with-a-single-site" class="section">
 
<div id="associating-content-with-a-single-site" class="section">
  
=== Associating content with a single site ===
+
=== 将内容与单个站点相关联 ===
  
Similarly, you can associate a model to the
+
同样,您可以使用 [[../../models/fields#django.db.models|ForeignKey]] 以多对一关系将模型与 [[#django.contrib.sites.models.Site|Site]] 模型相关联。
[[#django.contrib.sites.models.Site|<code>Site</code>]]
 
model in a many-to-one relationship, using
 
[[../../models/fields#django.db.models|<code>ForeignKey</code>]].
 
  
For example, if an article is only allowed on a single site, you'd use a model
+
例如,如果一篇文章只允许在一个站点上发布,您可以使用这样的模型:
like this:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第122行: 第95行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
 
from django.db import models
 
from django.db import models
  
第128行: 第101行:
 
     headline = models.CharField(max_length=200)
 
     headline = models.CharField(max_length=200)
 
     # ...
 
     # ...
     site = models.ForeignKey(Site, on_delete=models.CASCADE)</pre>
+
     site = models.ForeignKey(Site, on_delete=models.CASCADE)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This has the same benefits as described in the last section.
+
这具有与上一节中描述的相同的好处。
  
  
第140行: 第113行:
  
 
<span id="hooking-into-current-site-from-views"></span>
 
<span id="hooking-into-current-site-from-views"></span>
=== Hooking into the current site from views ===
+
=== 从视图连接到当前站点 ===
  
You can use the sites framework in your Django views to do
+
您可以在 Django 视图中使用站点框架,根据调用视图的站点执行特定操作。 例如:
particular things based on the site in which the view is being called.
 
For example:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第150行: 第121行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.conf import settings
+
<syntaxhighlight lang="python">from django.conf import settings
  
 
def my_view(request):
 
def my_view(request):
第158行: 第129行:
 
     else:
 
     else:
 
         # Do something else.
 
         # Do something else.
         pass</pre>
+
         pass</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Of course, it's ugly to hard-code the site IDs like that. This sort of
+
当然,像这样对站点 ID 进行硬编码是很丑陋的。 这种硬编码最适合您需要快速完成的黑客修复。 完成同样事情的更简洁的方法是检查当前站点的域:
hard-coding is best for hackish fixes that you need done quickly. The
 
cleaner way of accomplishing the same thing is to check the current site's
 
domain:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第172行: 第140行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.shortcuts import get_current_site
+
<syntaxhighlight lang="python">from django.contrib.sites.shortcuts import get_current_site
  
 
def my_view(request):
 
def my_view(request):
第181行: 第149行:
 
     else:
 
     else:
 
         # Do something else.
 
         # Do something else.
         pass</pre>
+
         pass</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This has also the advantage of checking if the sites framework is installed,
+
这还有一个好处是检查站点框架是否已安装,如果没有,则返回 [[#django.contrib.sites.requests.RequestSite|RequestSite]] 实例。
and return a [[#django.contrib.sites.requests.RequestSite|<code>RequestSite</code>]] instance if
 
it is not.
 
  
If you don't have access to the request object, you can use the
+
如果您无权访问请求对象,则可以使用 [[#django.contrib.sites.models.Site|Site]] 模型管理器的 <code>get_current()</code> 方法。 然后,您应该确保您的设置文件确实包含 [[#id3|:setting:`SITE_ID`]] 设置。 这个例子相当于上一个:
<code>get_current()</code> method of the [[#django.contrib.sites.models.Site|<code>Site</code>]]
 
model's manager. You should then ensure that your settings file does contain
 
the [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting. This example is equivalent to the previous one:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第199行: 第162行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
  
 
def my_function_without_request():
 
def my_function_without_request():
第208行: 第171行:
 
     else:
 
     else:
 
         # Do something else.
 
         # Do something else.
         pass</pre>
+
         pass</syntaxhighlight>
  
 
</div>
 
</div>
第217行: 第180行:
 
<div id="getting-the-current-domain-for-display" class="section">
 
<div id="getting-the-current-domain-for-display" class="section">
  
=== Getting the current domain for display ===
+
=== 获取当前域进行显示 ===
  
LJWorld.com and Lawrence.com both have email alert functionality, which lets
+
LJWorld.com Lawrence.com 都有电子邮件提醒功能,读者可以注册以在新闻发生时收到通知。 这是非常基本的:读者在网络表单上注册并立即收到一封电子邮件,内容是“感谢您的订阅。”
readers sign up to get notifications when news happens. It's pretty basic: A
 
reader signs up on a Web form and immediately gets an email saying,
 
&quot;Thanks for your subscription.&quot;
 
  
It'd be inefficient and redundant to implement this sign up processing code
+
两次实施此注册处理代码将是低效和冗余的,因此站点在幕后使用相同的代码。 但是每个站点的“感谢您注册”通知需要不同。 通过使用 [[#django.contrib.sites.models.Site|Site]] 对象,我们可以抽象出“谢谢”通知,以使用当前站点的 [[#django.contrib.sites.models.Site.name|name]] [[#django.contrib.sites.models.Site.domain|domain]] 的值。
twice, so the sites use the same code behind the scenes. But the &quot;thank you for
 
signing up&quot; notice needs to be different for each site. By using
 
[[#django.contrib.sites.models.Site|<code>Site</code>]]
 
objects, we can abstract the &quot;thank you&quot; notice to use the values of the
 
current site's [[#django.contrib.sites.models.Site.name|<code>name</code>]] and
 
[[#django.contrib.sites.models.Site.domain|<code>domain</code>]].
 
  
Here's an example of what the form-handling view looks like:
+
以下是表单处理视图的示例:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第238行: 第192行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.shortcuts import get_current_site
+
<syntaxhighlight lang="python">from django.contrib.sites.shortcuts import get_current_site
 
from django.core.mail import send_mail
 
from django.core.mail import send_mail
  
第255行: 第209行:
 
     )
 
     )
  
     # ...</pre>
+
     # ...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
On Lawrence.com, this email has the subject line &quot;Thanks for subscribing to
+
Lawrence.com 上,这封电子邮件的主题是“感谢您订阅 lawrence.com 警报”。 在 LJWorld.com 上,电子邮件的主题是“感谢您订阅 LJWorld.com 警报”。 电子邮件的消息正文也是如此。
lawrence.com alerts.&quot; On LJWorld.com, the email has the subject &quot;Thanks for
 
subscribing to LJWorld.com alerts.&quot; Same goes for the email's message body.
 
  
Note that an even more flexible (but more heavyweight) way of doing this would
+
请注意,一种更灵活(但更重量级)的方法是使用 Django 的模板系统。 假设 Lawrence.com LJWorld.com 有不同的模板目录( [[#id5|:setting:`目录 `]] ),您可以像这样使用模板系统:
be to use Django's template system. Assuming Lawrence.com and LJWorld.com have
 
different template directories ([[../../settings#std-setting-TEMPLATES-DIRS|<code>DIRS</code>]]), you could
 
farm out to the template system like so:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第273行: 第222行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.core.mail import send_mail
+
<syntaxhighlight lang="python">from django.core.mail import send_mail
 
from django.template import loader
 
from django.template import loader
  
第284行: 第233行:
 
     send_mail(subject, message, 'editor@ljworld.com', [user.email])
 
     send_mail(subject, message, 'editor@ljworld.com', [user.email])
  
     # ...</pre>
+
     # ...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
In this case, you'd have to create <code>subject.txt</code> and <code>message.txt</code>
+
在这种情况下,您必须为 LJWorld.com 和 Lawrence.com 模板目录创建 <code>subject.txt</code> <code>message.txt</code> 模板文件。 这为您提供了更大的灵活性,但也更复杂。
template files for both the LJWorld.com and Lawrence.com template directories.
 
That gives you more flexibility, but it's also more complex.
 
  
It's a good idea to exploit the [[#django.contrib.sites.models.Site|<code>Site</code>]]
+
最好尽可能多地利用 [[#django.contrib.sites.models.Site|Site]] 对象,以消除不需要的复杂性和冗余。
objects as much as possible, to remove unneeded complexity and redundancy.
 
  
  
第300行: 第246行:
 
<div id="getting-the-current-domain-for-full-urls" class="section">
 
<div id="getting-the-current-domain-for-full-urls" class="section">
  
=== Getting the current domain for full URLs ===
+
=== 获取完整 URL 的当前域 ===
  
Django's <code>get_absolute_url()</code> convention is nice for getting your objects'
+
Django <code>get_absolute_url()</code> 约定非常适合在不带域名的情况下获取对象的 URL,但在某些情况下,您可能希望显示完整的 URL - 带有 <code>http://</code> 和域和所有内容 - 用于对象. 为此,您可以使用站点框架。 一个例子:
URL without the domain name, but in some cases you might want to display the
 
full URL -- with <code>http://</code> and the domain and everything -- for an object.
 
To do this, you can use the sites framework. An example:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第311行: 第254行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">>>> from django.contrib.sites.models import Site
&gt;&gt;&gt; obj = MyModel.objects.get(id=3)
+
>>> obj = MyModel.objects.get(id=3)
&gt;&gt;&gt; obj.get_absolute_url()
+
>>> obj.get_absolute_url()
 
'/mymodel/objects/3/'
 
'/mymodel/objects/3/'
&gt;&gt;&gt; Site.objects.get_current().domain
+
>>> Site.objects.get_current().domain
 
'example.com'
 
'example.com'
&gt;&gt;&gt; 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
+
>>> 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'https://example.com/mymodel/objects/3/'</pre>
+
'https://example.com/mymodel/objects/3/'</syntaxhighlight>
  
 
</div>
 
</div>
第329行: 第272行:
 
<div id="enabling-the-sites-framework" class="section">
 
<div id="enabling-the-sites-framework" class="section">
  
<span id="id1"></span>
+
<span id="id7"></span>
== Enabling the sites framework ==
+
== 启用站点框架 ==
  
To enable the sites framework, follow these steps:
+
要启用站点框架,请按照下列步骤操作:
  
 
<ol>
 
<ol>
<li><p>Add <code>'django.contrib.sites'</code> to your [[../../settings#std-setting-INSTALLED_APPS|<code>INSTALLED_APPS</code>]] setting.</p></li>
+
<li><p><code>'django.contrib.sites'</code> 添加到您的 [[#id8|:setting:`INSTALLED_APPS`]] 设置。</p></li>
<li><p>Define a [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting:</p>
+
<li><p>定义一个 [[#id10|:setting:`SITE_ID`]] 设置:</p>
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SITE_ID = 1</pre>
+
<syntaxhighlight lang="python">SITE_ID = 1</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div></li>
 
</div></li>
<li><p>Run [[../../django-admin#django-admin-migrate|<code>migrate</code>]].</p></li></ol>
+
<li><p>运行 [[#id12|:djadmin:`migrate`]]</p></li></ol>
  
<code>django.contrib.sites</code> registers a
+
<code>django.contrib.sites</code> 注册一个 [[../../signals#django.db.models.signals|post_migrate]] 信号处理程序,它创建一个名为 <code>example.com</code> 的默认站点,域为 <code>example.com</code>。 该站点也将在 Django 创建测试数据库后创建。 要为您的项目设置正确的名称和域,您可以使用 [[../../../topics/migrations#data-migrations|数据迁移]]
[[../../signals#django.db.models.signals|<code>post_migrate</code>]] signal handler which creates a
 
default site named <code>example.com</code> with the domain <code>example.com</code>. This site
 
will also be created after Django creates the test database. To set the
 
correct name and domain for your project, you can use a [[../../../topics/migrations#data-migrations|<span class="std std-ref">data migration</span>]].
 
  
In order to serve different sites in production, you'd create a separate
+
为了在生产中为不同的站点提供服务,您需要为每个 <code>SITE_ID</code> 创建一个单独的设置文件(可能从通用设置文件导入以避免重复共享设置),然后指定适当的 <span id="index-0" class="target"></span> [[../../../topics/settings#envvar-DJANGO_SETTINGS_MODULE|DJANGO_SETTINGS_MODULE]] 每个站点。
settings file with each <code>SITE_ID</code> (perhaps importing from a common settings
 
file to avoid duplicating shared settings) and then specify the appropriate
 
<span id="index-0" class="target"></span>[[../../../topics/settings#envvar-DJANGO_SETTINGS_MODULE|<code>DJANGO_SETTINGS_MODULE</code>]] for each site.
 
  
  
第363行: 第299行:
 
<div id="caching-the-current-site-object" class="section">
 
<div id="caching-the-current-site-object" class="section">
  
== Caching the current <code>Site</code> object ==
+
== 缓存当前 Site 对象 ==
  
As the current site is stored in the database, each call to
+
由于当前站点存储在数据库中,因此每次调用 <code>Site.objects.get_current()</code> 都可能导致数据库查询。 但是 Django 比这更聪明一点:在第一次请求时,当前站点被缓存,任何后续调用都返回缓存的数据而不是访问数据库。
<code>Site.objects.get_current()</code> could result in a database query. But Django is a
 
little cleverer than that: on the first request, the current site is cached, and
 
any subsequent call returns the cached data instead of hitting the database.
 
  
If for any reason you want to force a database query, you can tell Django to
+
如果出于任何原因要强制执行数据库查询,您可以使用 <code>Site.objects.clear_cache()</code> 告诉 Django 清除缓存:
clear the cache using <code>Site.objects.clear_cache()</code>:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第377行: 第309行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre># First call; current site fetched from database.
+
<syntaxhighlight lang="python"># First call; current site fetched from database.
 
current_site = Site.objects.get_current()
 
current_site = Site.objects.get_current()
 
# ...
 
# ...
第387行: 第319行:
 
# Force a database query for the third call.
 
# Force a database query for the third call.
 
Site.objects.clear_cache()
 
Site.objects.clear_cache()
current_site = Site.objects.get_current()</pre>
+
current_site = Site.objects.get_current()</syntaxhighlight>
  
 
</div>
 
</div>
第396行: 第328行:
 
<div id="the-currentsitemanager" class="section">
 
<div id="the-currentsitemanager" class="section">
  
== The <code>CurrentSiteManager</code> ==
+
== CurrentSiteManager ==
  
; ''class'' <code>managers.</code><code>CurrentSiteManager</code>
+
; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">managers.</span></span><span class="sig-name descname"><span class="pre">CurrentSiteManager</span></span>
 
:  
 
:  
  
If [[#django.contrib.sites.models.Site|<code>Site</code>]] plays a key role in your
+
如果 [[#django.contrib.sites.models.Site|Site]] 在您的应用程序中发挥关键作用,请考虑在您的模型中使用有用的 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]]。 它是一个模型 [[../../../topics/db/managers|manager]],它自动过滤其查询以仅包括与当前 [[#django.contrib.sites.models.Site|Site]] 关联的对象。
application, consider using the helpful
 
[[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]] in your
 
model(s). It's a model [[../../../topics/db/managers|<span class="doc">manager</span>]] that
 
automatically filters its queries to include only objects associated
 
with the current [[#django.contrib.sites.models.Site|<code>Site</code>]].
 
  
 
<div class="admonition-mandatory-setting-site-id admonition">
 
<div class="admonition-mandatory-setting-site-id admonition">
  
Mandatory [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]]
+
强制 [[#id14|:设置:`SITE_ID`]]
  
The <code>CurrentSiteManager</code> is only usable when the [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]]
+
<code>CurrentSiteManager</code> 仅在您的设置中定义了 [[#id16|:setting:`SITE_ID`]] 设置时可用。
setting is defined in your settings.
 
  
  
 
</div>
 
</div>
Use [[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]] by adding it to
+
通过将 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 显式添加到您的模型来使用它。 例如:
your model explicitly. For example:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第424行: 第349行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
 
from django.contrib.sites.managers import CurrentSiteManager
 
from django.contrib.sites.managers import CurrentSiteManager
 
from django.db import models
 
from django.db import models
第434行: 第359行:
 
     site = models.ForeignKey(Site, on_delete=models.CASCADE)
 
     site = models.ForeignKey(Site, on_delete=models.CASCADE)
 
     objects = models.Manager()
 
     objects = models.Manager()
     on_site = CurrentSiteManager()</pre>
+
     on_site = CurrentSiteManager()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With this model, <code>Photo.objects.all()</code> will return all <code>Photo</code> objects in
+
使用此模型,<code>Photo.objects.all()</code> 将返回数据库中的所有 <code>Photo</code> 对象,但 <code>Photo.on_site.all()</code> 将仅返回与当前站点关联的 <code>Photo</code> 对象,根据[[#id18|:setting:`SITE_ID`]] 设置。
the database, but <code>Photo.on_site.all()</code> will return only the <code>Photo</code> objects
 
associated with the current site, according to the [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting.
 
  
Put another way, these two statements are equivalent:
+
换句话说,这两个语句是等价的:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第449行: 第372行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Photo.objects.filter(site=settings.SITE_ID)
+
<syntaxhighlight lang="python">Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()</pre>
+
Photo.on_site.all()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
How did [[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]]
+
[[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 如何知道 <code>Photo</code> 的哪个字段是 [[#django.contrib.sites.models.Site|Site]]? 默认情况下,[[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 查找名为 <code>site</code> [[../../models/fields#django.db.models|ForeignKey]] 或名为 <code>sites</code> [[../../models/fields#django.db.models|ManyToManyField]] 以进行过滤。 如果您使用名为 <code>site</code> <code>sites</code> 以外的名称的字段来标识您的对象与哪些 [[#django.contrib.sites.models.Site|Site]] 对象相关,则您需要将自定义字段名称显式传递为模型上 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 的参数。 以下模型具有名为 <code>publish_on</code> 的字段,演示了这一点:
know which field of <code>Photo</code> was the
 
[[#django.contrib.sites.models.Site|<code>Site</code>]]? By default,
 
[[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]] looks for a
 
either a [[../../models/fields#django.db.models|<code>ForeignKey</code>]] called
 
<code>site</code> or a
 
[[../../models/fields#django.db.models|<code>ManyToManyField</code>]] called
 
<code>sites</code> to filter on. If you use a field named something other than
 
<code>site</code> or <code>sites</code> to identify which
 
[[#django.contrib.sites.models.Site|<code>Site</code>]] objects your object is
 
related to, then you need to explicitly pass the custom field name as
 
a parameter to
 
[[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]] on your
 
model. The following model, which has a field called <code>publish_on</code>,
 
demonstrates this:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第475行: 第384行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
 
from django.contrib.sites.managers import CurrentSiteManager
 
from django.contrib.sites.managers import CurrentSiteManager
 
from django.db import models
 
from django.db import models
第485行: 第394行:
 
     publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
 
     publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
 
     objects = models.Manager()
 
     objects = models.Manager()
     on_site = CurrentSiteManager('publish_on')</pre>
+
     on_site = CurrentSiteManager('publish_on')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you attempt to use [[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]]
+
如果您尝试使用 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 并传递一个不存在的字段名称,Django 将引发 <code>ValueError</code>
and pass a field name that doesn't exist, Django will raise a <code>ValueError</code>.
 
  
Finally, note that you'll probably want to keep a normal
+
最后,请注意,即使您使用 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]],您也可能希望在模型上保留一个正常的(非站点特定的)<code>Manager</code>。 如 [[../../../topics/db/managers|manager 文档]] 中所述,如果您手动定义管理器,那么 Django 将不会为您创建自动 <code>objects = models.Manager()</code> 管理器。 还要注意 Django 的某些部分——即 Django 管理站点和通用视图——使用模型中定义的 ''first'' 中的任何管理器,所以如果你希望你的管理站点可以访问所有对象(不仅仅是站点特定的),在定义 [[#django.contrib.sites.managers.CurrentSiteManager|CurrentSiteManager]] 之前,将 <code>objects = models.Manager()</code> 放入模型中。
(non-site-specific) <code>Manager</code> on your model, even if you use
 
[[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]]. As
 
explained in the [[../../../topics/db/managers|<span class="doc">manager documentation</span>]], if
 
you define a manager manually, then Django won't create the automatic
 
<code>objects = models.Manager()</code> manager for you. Also note that certain
 
parts of Django -- namely, the Django admin site and generic views --
 
use whichever manager is defined ''first'' in the model, so if you want
 
your admin site to have access to all objects (not just site-specific
 
ones), put <code>objects = models.Manager()</code> in your model, before you
 
define [[#django.contrib.sites.managers.CurrentSiteManager|<code>CurrentSiteManager</code>]].
 
  
  
第509行: 第407行:
 
<div id="site-middleware" class="section">
 
<div id="site-middleware" class="section">
  
<span id="id2"></span>
+
<span id="id20"></span>
== Site middleware ==
+
== 站点中间件 ==
  
If you often use this pattern:
+
如果你经常使用这种模式:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第518行: 第416行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.contrib.sites.models import Site
+
<syntaxhighlight lang="python">from django.contrib.sites.models import Site
  
 
def my_view(request):
 
def my_view(request):
 
     site = Site.objects.get_current()
 
     site = Site.objects.get_current()
     ...</pre>
+
     ...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
To avoid repetitions, add
+
为避免重复,将 [[../../middleware#django.contrib.sites.middleware|django.contrib.sites.middleware.CurrentSiteMiddleware]] 添加到 [[#id21|:setting:`MIDDLEWARE`]]。 中间件在每个请求对象上设置 <code>site</code> 属性,因此您可以使用 <code>request.site</code> 获取当前站点。
[[../../middleware#django.contrib.sites.middleware|<code>django.contrib.sites.middleware.CurrentSiteMiddleware</code>]] to
 
[[../../settings#std-setting-MIDDLEWARE|<code>MIDDLEWARE</code>]]. The middleware sets the <code>site</code> attribute on every
 
request object, so you can use <code>request.site</code> to get the current site.
 
  
  
第536行: 第431行:
 
<div id="how-django-uses-the-sites-framework" class="section">
 
<div id="how-django-uses-the-sites-framework" class="section">
  
== How Django uses the sites framework ==
+
== Django 如何使用站点框架 ==
  
Although it's not required that you use the sites framework, it's strongly
+
尽管不要求您使用站点框架,但强烈建议您使用它,因为 Django 在一些地方利用了它。 即使您的 Django 安装仅支持单个站点,您也应该花两秒钟时间使用 <code>domain</code> <code>name</code> 创建站点对象,并在 [[#id23|中指向其 ID:设置:`SITE_ID`]] 设置。
encouraged, because Django takes advantage of it in a few places. Even if your
 
Django installation is powering only a single site, you should take the two
 
seconds to create the site object with your <code>domain</code> and <code>name</code>, and point
 
to its ID in your [[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting.
 
  
Here's how Django uses the sites framework:
+
以下是 Django 使用站点框架的方式:
  
* In the [[../redirects#module-django.contrib|<code>redirects framework</code>]], each redirect object is associated with a particular site. When Django searches for a redirect, it takes into account the current site.
+
* [[../redirects#module-django.contrib|重定向框架]] 中,每个重定向对象都与特定站点相关联。 当 Django 搜索重定向时,它会考虑当前站点。
* In the [[../flatpages#module-django.contrib|<code>flatpages framework</code>]], each flatpage is associated with a particular site. When a flatpage is created, you specify its [[#django.contrib.sites.models.Site|<code>Site</code>]], and the [[../flatpages#django.contrib.flatpages.middleware|<code>FlatpageFallbackMiddleware</code>]] checks the current site in retrieving flatpages to display.
+
* [[../flatpages#module-django.contrib|flatpages 框架]] 中,每个flatpage 都与一个特定的站点相关联。 创建平面页面时,您指定其 [[#django.contrib.sites.models.Site|Site]],并且 [[../flatpages#django.contrib.flatpages.middleware|FlatpageFallbackMiddleware]] 在检索要显示的平面页面时检查当前站点。
* In the [[../syndication#module-django.contrib|<code>syndication framework</code>]], the templates for <code>title</code> and <code>description</code> automatically have access to a variable <code>{{ site }}</code>, which is the [[#django.contrib.sites.models.Site|<code>Site</code>]] object representing the current site. Also, the hook for providing item URLs will use the <code>domain</code> from the current [[#django.contrib.sites.models.Site|<code>Site</code>]] object if you don't specify a fully-qualified domain.
+
* [[../syndication#module-django.contrib|联合框架]] 中,<code>title</code> <code>description</code> 的模板自动可以访问变量 <code>{{ site }}</code>,即 [[#django.contrib.sites.models.Site|Site]]表示当前站点的对象。 此外,如果您未指定完全限定的域,则用于提供项目 URL 的挂钩将使用当前 [[#django.contrib.sites.models.Site|Site]] 对象中的 <code>domain</code>
* In the [[../../../topics/auth/index#module-django.contrib|<code>authentication framework</code>]], [[../../../topics/auth/default#django.contrib.auth.views|<code>django.contrib.auth.views.LoginView</code>]] passes the current [[#django.contrib.sites.models.Site|<code>Site</code>]] name to the template as <code>{{ site_name }}</code>.
+
* [[../../../topics/auth/index#module-django.contrib|认证框架]]中,[[../../../topics/auth/default#django.contrib.auth.views|django.contrib.auth.views.LoginView]]将当前的[[#django.contrib.sites.models.Site|站点]]名称作为<code>{{ site_name }}</code>传递给模板。
* The shortcut view (<code>django.contrib.contenttypes.views.shortcut</code>) uses the domain of the current [[#django.contrib.sites.models.Site|<code>Site</code>]] object when calculating an object's URL.
+
* 快捷视图 (<code>django.contrib.contenttypes.views.shortcut</code>) 在计算对象的 URL 时使用当前 [[#django.contrib.sites.models.Site|Site]] 对象的域。
* In the admin framework, the &quot;view on site&quot; link uses the current [[#django.contrib.sites.models.Site|<code>Site</code>]] to work out the domain for the site that it will redirect to.
+
* 在管理框架中,“现场查看”链接使用当前的 [[#django.contrib.sites.models.Site|站点]] 来计算将重定向到的站点的域。
  
  
第557行: 第448行:
 
<div id="requestsite-objects" class="section">
 
<div id="requestsite-objects" class="section">
  
== <code>RequestSite</code> objects ==
+
== RequestSite 物体 ==
  
Some [[../index|<span class="doc">django.contrib</span>]] applications take advantage of
+
某些 [[../index|django.contrib]] 应用程序利用了站点框架,但其架构方式并不 ''需要'' 将站点框架安装到您的数据库中。 (有些人不想,或者只是 ''无法'' 安装站点框架所需的额外数据库表。)对于这些情况,框架提供了 [[#django.contrib.sites.requests.RequestSite|django.contrib.sites .requests.RequestSite]] 类,当数据库支持的站点框架不可用时,它可以用作后备。
the sites framework but are architected in a way that doesn't ''require'' the
 
sites framework to be installed in your database. (Some people don't want to,
 
or just aren't ''able'' to install the extra database table that the sites
 
framework requires.) For those cases, the framework provides a
 
[[#django.contrib.sites.requests.RequestSite|<code>django.contrib.sites.requests.RequestSite</code>]] class, which can be used as
 
a fallback when the database-backed sites framework is not available.
 
  
; ''class'' <code>requests.</code><code>RequestSite</code>
+
; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">requests.</span></span><span class="sig-name descname"><span class="pre">RequestSite</span></span>
: A class that shares the primary interface of [[#django.contrib.sites.models.Site|<code>Site</code>]] (i.e., it has <code>domain</code> and <code>name</code> attributes) but gets its data from a Django [[../../request-response#django.http|<code>HttpRequest</code>]] object rather than from a database.
+
: 一个类共享 [[#django.contrib.sites.models.Site|Site]] 的主要接口(即,它具有 <code>domain</code> <code>name</code> 属性)但从 Django [[../../request-response#django.http|HttpRequest]] 对象获取其数据而不是来自数据库。
;; <code>__init__</code><span class="sig-paren">(</span>''<span class="n">request</span>''<span class="sig-paren">)</span>
+
;; <span class="sig-name descname"><span class="pre">__init__</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">request</span></span>''<span class="sig-paren">)</span>
;: Sets the <code>name</code> and <code>domain</code> attributes to the value of [[../../request-response#django.http.HttpRequest|<code>get_host()</code>]].
+
;: <code>name</code> <code>domain</code> 属性设置为 [[../../request-response#django.http.HttpRequest|get_host()]] 的值。
  
A [[#django.contrib.sites.requests.RequestSite|<code>RequestSite</code>]] object has a similar
+
[[#django.contrib.sites.requests.RequestSite|RequestSite]] 对象与普通的 [[#django.contrib.sites.models.Site|Site]] 对象具有相似的接口,除了它的 [[#django.contrib.sites.requests.RequestSite.__init__|__init__()]] 方法采用 [[../../request-response#django.http|HttpRequest]] 对象。 它可以通过查看请求的域来推断出 <code>domain</code> <code>name</code>。 它有 <code>save()</code> <code>delete()</code> 方法来匹配 [[#django.contrib.sites.models.Site|Site]] 的接口,但是这些方法增加了 <code>NotImplementedError</code>
interface to a normal [[#django.contrib.sites.models.Site|<code>Site</code>]] object,
 
except its [[#django.contrib.sites.requests.RequestSite.__init__|<code>__init__()</code>]]
 
method takes an [[../../request-response#django.http|<code>HttpRequest</code>]] object. It's able to deduce
 
the <code>domain</code> and <code>name</code> by looking at the request's domain. It has
 
<code>save()</code> and <code>delete()</code> methods to match the interface of
 
[[#django.contrib.sites.models.Site|<code>Site</code>]], but the methods raise
 
<code>NotImplementedError</code>.
 
  
  
第585行: 第463行:
 
<div id="get-current-site-shortcut" class="section">
 
<div id="get-current-site-shortcut" class="section">
  
== <code>get_current_site</code> shortcut ==
+
== get_current_site 快捷键 ==
  
Finally, to avoid repetitive fallback code, the framework provides a
+
最后,为了避免重复的回退代码,框架提供了一个 [[#django.contrib.sites.shortcuts.get_current_site|django.contrib.sites.shortcuts.get_current_site()]] 函数。
[[#django.contrib.sites.shortcuts.get_current_site|<code>django.contrib.sites.shortcuts.get_current_site()</code>]] function.
 
  
 
<dl>
 
<dl>
<dt><code>shortcuts.</code><code>get_current_site</code><span class="sig-paren">(</span>''<span class="n">request</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">shortcuts.</span></span><span class="sig-name descname"><span class="pre">get_current_site</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">request</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>A function that checks if <code>django.contrib.sites</code> is installed and
+
<dd><p>检查是否安装了 <code>django.contrib.sites</code> 并根据请求返回当前 [[#django.contrib.sites.models.Site|Site]] 对象或 [[#django.contrib.sites.requests.RequestSite|RequestSite]] 对象的函数。 如果未定义 [[#id26|:setting:`SITE_ID`]] 设置,它会根据 [[../../request-response#django.http.HttpRequest|request.get_host()]] 查找当前站点。</p>
returns either the current [[#django.contrib.sites.models.Site|<code>Site</code>]]
+
<p>[[../../request-response#django.http.HttpRequest|request.get_host()]] Host 标头明确指定了端口时,域和端口都可以返回,例如 <code>example.com:80</code>。 在这种情况下,如果由于主机与数据库中的记录不匹配而导致查找失败,则会剥离端口并仅使用域部分重试查找。 这不适用于将始终使用未修改主机的 [[#django.contrib.sites.requests.RequestSite|RequestSite]]</p></dd></dl>
object or a [[#django.contrib.sites.requests.RequestSite|<code>RequestSite</code>]] object
 
based on the request. It looks up the current site based on
 
[[../../request-response#django.http.HttpRequest|<code>request.get_host()</code>]] if the
 
[[../../settings#std-setting-SITE_ID|<code>SITE_ID</code>]] setting is not defined.</p>
 
<p>Both a domain and a port may be returned by [[../../request-response#django.http.HttpRequest|<code>request.get_host()</code>]] when the Host header has a port
 
explicitly specified, e.g. <code>example.com:80</code>. In such cases, if the
 
lookup fails because the host does not match a record in the database,
 
the port is stripped and the lookup is retried with the domain part
 
only. This does not apply to
 
[[#django.contrib.sites.requests.RequestSite|<code>RequestSite</code>]] which will always
 
use the unmodified host.</p></dd></dl>
 
  
  
第610行: 第476行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Django 3.0.x 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Django 3.0.x 文档]]

2021年10月31日 (日) 04:09的最新版本

“站点”框架

Django 带有一个可选的“站点”框架。 它是一个用于将对象和功能与特定网站相关联的钩子,它是您的 Django 站点的域名和“详细”名称的存放处。

如果您的单个 Django 安装支持多个站点,并且您需要以某种方式区分这些站点,请使用它。

站点框架主要基于这个模型:

class models.Site

用于存储网站的 domainname 属性的模型。

domain

与网站关联的完全限定域名。 例如,www.example.com

name

网站的人类可读的“详细”名称。

:setting:`SITE_ID` 设置指定与该特定设置文件关联的 Site 对象的数据库 ID。 如果省略该设置,get_current_site() 函数将尝试通过将 domainrequest.get_host() 中的主机名进行比较来获取当前站点] 方法。

你如何使用它取决于你,但 Django 通过一些约定自动以几种方式使用它。

示例用法

你为什么要使用网站? 最好通过例子来解释。

将内容与多个站点相关联

LJWorld.comLawrence.com 网站由同一新闻机构运营 - 位于堪萨斯州劳伦斯的劳伦斯世界日报。 LJWorld.com 专注于新闻,而 Lawrence.com 专注于本地娱乐。 但有时编辑想在 站点上发表文章。

解决这个问题的天真的方法是要求网站制作人将同一个故事发布两次:一次是在 LJWorld.com,一次是在 Lawrence.com。 但这对网站制作者来说效率低下,而且在数据库中存储同一故事的多个副本是多余的。

一个更好的解决方案消除了内容重复:两个站点使用相同的文章数据库,并且一篇文章与一个或多个站点相关联。 在 Django 模型术语中,这由 Article 模型中的 ManyToManyField 表示:

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")
        # ...


将内容与单个站点相关联

同样,您可以使用 ForeignKey 以多对一关系将模型与 Site 模型相关联。

例如,如果一篇文章只允许在一个站点上发布,您可以使用这样的模型:

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 实例。

如果您无权访问请求对象,则可以使用 Site 模型管理器的 get_current() 方法。 然后,您应该确保您的设置文件确实包含 :setting:`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 对象,我们可以抽象出“谢谢”通知,以使用当前站点的 namedomain 的值。

以下是表单处理视图的示例:

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 警报”。 在 LJWorld.com 上,电子邮件的主题是“感谢您订阅 LJWorld.com 警报”。 电子邮件的消息正文也是如此。

请注意,一种更灵活(但更重量级)的方法是使用 Django 的模板系统。 假设 Lawrence.com 和 LJWorld.com 有不同的模板目录( :setting:`目录 ` ),您可以像这样使用模板系统:

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])

    # ...

在这种情况下,您必须为 LJWorld.com 和 Lawrence.com 模板目录创建 subject.txtmessage.txt 模板文件。 这为您提供了更大的灵活性,但也更复杂。

最好尽可能多地利用 Site 对象,以消除不需要的复杂性和冗余。


获取完整 URL 的当前域

Django 的 get_absolute_url() 约定非常适合在不带域名的情况下获取对象的 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/'

启用站点框架

要启用站点框架,请按照下列步骤操作:

  1. 'django.contrib.sites' 添加到您的 :setting:`INSTALLED_APPS` 设置。

  2. 定义一个 :setting:`SITE_ID` 设置:

    SITE_ID = 1
  3. 运行 :djadmin:`migrate`

django.contrib.sites 注册一个 post_migrate 信号处理程序,它创建一个名为 example.com 的默认站点,域为 example.com。 该站点也将在 Django 创建测试数据库后创建。 要为您的项目设置正确的名称和域,您可以使用 数据迁移

为了在生产中为不同的站点提供服务,您需要为每个 SITE_ID 创建一个单独的设置文件(可能从通用设置文件导入以避免重复共享设置),然后指定适当的 DJANGO_SETTINGS_MODULE 每个站点。


缓存当前 Site 对象

由于当前站点存储在数据库中,因此每次调用 Site.objects.get_current() 都可能导致数据库查询。 但是 Django 比这更聪明一点:在第一次请求时,当前站点被缓存,任何后续调用都返回缓存的数据而不是访问数据库。

如果出于任何原因要强制执行数据库查询,您可以使用 Site.objects.clear_cache() 告诉 Django 清除缓存:

# 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

class managers.CurrentSiteManager

如果 Site 在您的应用程序中发挥关键作用,请考虑在您的模型中使用有用的 CurrentSiteManager。 它是一个模型 manager,它自动过滤其查询以仅包括与当前 Site 关联的对象。

强制 :设置:`SITE_ID`

CurrentSiteManager 仅在您的设置中定义了 :setting:`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 对象,根据:setting:`SITE_ID` 设置。

换句话说,这两个语句是等价的:

Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()

CurrentSiteManager 如何知道 Photo 的哪个字段是 Site? 默认情况下,CurrentSiteManager 查找名为 siteForeignKey 或名为 sitesManyToManyField 以进行过滤。 如果您使用名为 sitesites 以外的名称的字段来标识您的对象与哪些 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

最后,请注意,即使您使用 CurrentSiteManager,您也可能希望在模型上保留一个正常的(非站点特定的)Manager。 如 manager 文档 中所述,如果您手动定义管理器,那么 Django 将不会为您创建自动 objects = models.Manager() 管理器。 还要注意 Django 的某些部分——即 Django 管理站点和通用视图——使用模型中定义的 first 中的任何管理器,所以如果你希望你的管理站点可以访问所有对象(不仅仅是站点特定的),在定义 CurrentSiteManager 之前,将 objects = models.Manager() 放入模型中。


站点中间件

如果你经常使用这种模式:

from django.contrib.sites.models import Site

def my_view(request):
    site = Site.objects.get_current()
    ...

为避免重复,将 django.contrib.sites.middleware.CurrentSiteMiddleware 添加到 :setting:`MIDDLEWARE`。 中间件在每个请求对象上设置 site 属性,因此您可以使用 request.site 获取当前站点。


Django 如何使用站点框架

尽管不要求您使用站点框架,但强烈建议您使用它,因为 Django 在一些地方利用了它。 即使您的 Django 安装仅支持单个站点,您也应该花两秒钟时间使用 domainname 创建站点对象,并在 中指向其 ID:设置:`SITE_ID` 设置。

以下是 Django 使用站点框架的方式:

  • 重定向框架 中,每个重定向对象都与特定站点相关联。 当 Django 搜索重定向时,它会考虑当前站点。
  • flatpages 框架 中,每个flatpage 都与一个特定的站点相关联。 创建平面页面时,您指定其 Site,并且 FlatpageFallbackMiddleware 在检索要显示的平面页面时检查当前站点。
  • 联合框架 中,titledescription 的模板自动可以访问变量 模板:Site,即 Site表示当前站点的对象。 此外,如果您未指定完全限定的域,则用于提供项目 URL 的挂钩将使用当前 Site 对象中的 domain
  • 认证框架中,django.contrib.auth.views.LoginView将当前的站点名称作为模板:Site name传递给模板。
  • 快捷视图 (django.contrib.contenttypes.views.shortcut) 在计算对象的 URL 时使用当前 Site 对象的域。
  • 在管理框架中,“现场查看”链接使用当前的 站点 来计算将重定向到的站点的域。


RequestSite 物体

某些 django.contrib 应用程序利用了站点框架,但其架构方式并不 需要 将站点框架安装到您的数据库中。 (有些人不想,或者只是 无法 安装站点框架所需的额外数据库表。)对于这些情况,框架提供了 django.contrib.sites .requests.RequestSite 类,当数据库支持的站点框架不可用时,它可以用作后备。

class requests.RequestSite
一个类共享 Site 的主要接口(即,它具有 domainname 属性)但从 Django HttpRequest 对象获取其数据而不是来自数据库。
__init__(request)
namedomain 属性设置为 get_host() 的值。

RequestSite 对象与普通的 Site 对象具有相似的接口,除了它的 __init__() 方法采用 HttpRequest 对象。 它可以通过查看请求的域来推断出 domainname。 它有 save()delete() 方法来匹配 Site 的接口,但是这些方法增加了 NotImplementedError


get_current_site 快捷键

最后,为了避免重复的回退代码,框架提供了一个 django.contrib.sites.shortcuts.get_current_site() 函数。

shortcuts.get_current_site(request)

检查是否安装了 django.contrib.sites 并根据请求返回当前 Site 对象或 RequestSite 对象的函数。 如果未定义 :setting:`SITE_ID` 设置,它会根据 request.get_host() 查找当前站点。

request.get_host() 当 Host 标头明确指定了端口时,域和端口都可以返回,例如 example.com:80。 在这种情况下,如果由于主机与数据库中的记录不匹配而导致查找失败,则会剥离端口并仅使用域部分重试查找。 这不适用于将始终使用未修改主机的 RequestSite