多个站点

Django 的多站点系统是一种通用框架,它让你可以在同一个数据库和同一个Django项目下操作多个网站。 这是一个抽象概念,理解起来可能有点困难,因此我们从几个让它能派上用场的实际情景入手。

情景1:多站点间复用数据

正如我们在第一章里所讲,Django 构建的网站 LJWorld.com 和 Lawrance.com 是用由同一个新闻组织控制的: 肯萨斯州劳伦斯市的 劳伦斯日报世界 报纸。 LJWorld.com 主要做新闻,而 Lawrence.com 关注本地娱乐。 然而有时,编辑可能需要把一篇文章发布到 两个 网站上。

解决此问题的死脑筋方法可能是使用每个站点分别使用不同的数据库,然后要求站点维护者把同一篇文章发布两次: 一次为 LJWorld.com,另一次为Lawrence.com。 但这对站点管理员来说是低效率的,而且为同一篇文章在数据库里保留多个副本也显得多余。

更好的解决方案? 两个网站用的是同一个文章数据库,并将每一篇文章与一个或多个站点用多对多关系关联起来。 Django 站点框架提供数据库表来记载哪些文章可以被关联。 它是一个把数据与一个或多个站点关联起来的钩子。

情景2:把网站的名字/域名保存在一个地方

LJWorld.com 和 Lawrence.com 都有邮件提醒功能,使读者注册后可以在新闻发生后立即收到通知。 这是一种完美的的机制: 某读者提交了注册表单,然后马上就受到一封内容是“感谢您的注册”的邮件。

把这个注册过程的代码实现两遍显然是低效、多余的,因此两个站点在后台使用相同的代码。 但感谢注册的通知在两个网站中需要不同。 通过使用 Site 对象,我们通过使用当前站点的 name (例如 'LJWorld.com' )和 domain(例如 'www.ljworld.com' )可以把感谢通知抽提出来。

Django 的多站点框架为你提供了一个位置来存储 Django 项目中每个站点的 name 和 domain ,这意味着你可以用同样的方法来重用这些值。

如何使用多站点框架

多站点框架与其说是一个框架,不如说是一系列约定。 所有的一切都基于两个简单的概念:

  • 位于 django.contrib.sites 的 Site 模型有 domain 和 name 两个字段。

  • SITE_ID 设置指定了与特定配置文件相关联的 Site 对象之数据库 ID。

如何运用这两个概念由你决定,但 Django 是通过几个简单的约定自动使用的。

安装多站点应用要执行以下几个步骤:

  1. 将 'django.contrib.sites' 加入到 INSTALLED_APPS 中。

  1. 运行 manage.py syncdb 命令将 django_site 表安装到数据库中。 这样也会建立默认的站点对象,域名为 example.com。

  1. example.com改成你自己的域名,然后通过Django admin站点或Python API来添加其他Site对象。 为该 Django 项目支撑的每个站(或域)创建一个 Site 对象。

  1. 在每个设置文件中定义一个 SITE_ID 变量。 该变量值应当是该设置文件所支撑的站点Site 对象的数据库 ID。

多站点框架的功能

下面几节讲述的是用多站点框架能够完成的几项工作。

多个站点的数据重用

正如在情景一中所解释的,要在多个站点间重用数据,仅需在模型中为 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)

这是在数据库中为多个站点进行文章关联操作的基础步骤。 在适当的位置使用该技术,你可以在多个站点中重复使用同一段 Django 视图代码。 继续 Article 模型范例,下面是一个可能的 article_detail 视图:

from django.conf import settings
from django.shortcuts import get_object_or_404
from mysite.articles.models import Article def article_detail(request, article_id):
a = get_object_or_404(Article, id=article_id, sites__id=settings.SITE_ID)
# ...

该视图方法是可重用的,因为它根据 SITE_ID 设置的值动态检查 articles 站点。

例如, LJWorld.coms 设置文件中有有个 SITE_ID 设置为 1 ,而 Lawrence.coms 设置文件中有个 SITE_ID 设置为 2 。如果该视图在 LJWorld.coms 处于激活状态时被调用,那么它将把查找范围局限于站点列表包括 LJWorld.com 在内的文章。

将内容与单一站点相关联

同样,你也可以使用 外键 在多对一关系中将一个模型关联到 Site 模型。

举例来说,如果某篇文章仅仅能够出现在一个站点上,你可以使用下面这样的模型:

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)

这与前一节中介绍的一样有益。

从视图钩挂当前站点

在底层,通过在 Django 视图中使用多站点框架,你可以让视图根据调用站点不同而完成不同的工作,例如:

from django.conf import settings

def my_view(request):
if settings.SITE_ID == 3:
# Do something.
else:
# Do something else.

当然,像那样对站点 ID 进行硬编码是比较难看的。 略为简洁的完成方式是查看当前的站点域:

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':
# Do something
else:
# Do something else.

从 Site 对象中获取 settings.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':
# Do something
else:
# Do something else.

注意

在这个最后的例子里,你不用导入 django.conf.settings 。

获取当前域用于呈现

正如情景二中所解释的那样,依据DRY原则(不做重复工作),你只需在一个位置储存站名和域名,然后引用当前 Site 对象的 name 和 domain 。例如: 例如:

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 例子,在Lawrence.com 该邮件的标题行是“感谢注册 Lawrence.com 提醒信件”。 在 LJWorld.com ,该邮件标题行是“感谢注册 LJWorld.com 提醒信件”。 这种站点关联行为方式对邮件信息主体也同样适用。

完成这项工作的一种更加灵活(但更重量级)的方法是使用 Django 的模板系统。 假定 Lawrence.com 和 LJWorld.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])
# ...

本例中,你不得不在 LJWorld.com 和 Lawrence.com 的模板目录中都创建一份 subject.txt 和 message.txt 模板。 正如之前所说,该方法带来了更大的灵活性,但也带来了更多复杂性。

尽可能多的利用 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() 仅根据 SITE_ID设置返回与当前站点相关联的 Photo 对象。

换言之,以下两条语句是等效的:

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

CurrentSiteManager 是如何知道 Photo 的哪个字段是 Site 呢?缺省情况下,它会查找一个叫做 site 的字段。如果你的模型包含了名字不是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 异常。

注意

即便是已经使用了 CurrentSiteManager ,你也许还想在模型中拥有一个正常的(非站点相关)的 管理器 。正如在附录 B 中所解释的,如果你手动定义了一个管理器,那么 Django 不会为你创建全自动的 objects = models.Manager() 管理器。

同样,Django 的特定部分(即 Django 超级管理站点和通用视图)使用在模型中定义 的第一个管理器,因此如果希望管理站点能够访问所有对象(而不是仅仅站点特有对象),请于定义 CurrentSiteManager 之前在模型中放入 objects = models.Manager() 。

Django如何使用多站点框架

尽管并不是必须的,我们还是强烈建议使用多站点框架,因为 Django 在几个地方利用了它。 即使只用 Django 来支持单个网站,你也应该花一点时间用 domain 和 name 来创建站点对象,并将 SITE_ID 设置指向它的 ID 。

 

以下讲述的是 Django 如何使用多站点框架:

  • 在重定向框架中(见后面的重定向一节),每一个重定向对象都与一个特定站点关联。 当 Django 搜索重定向的时候,它会考虑当前的 SITE_ID 。

  • 在注册框架中,每个注释都与特定站点相关。 每个注释被显示时,其 site 被设置为当前的 SITE_ID ,而当通过适当的模板标签列出注释时,只有当前站点的注释将会显示。

  • 在 flatpages 框架中 (参见后面的 Flatpages 一节),每个 flatpage 都与特定的站点相关联。 创建 flatpage 时,你都将指定它的 site ,而 flatpage 中间件在获取 flatpage 以显示它的过程中,将查看当前的 SITE_ID 。

     
  • 在 syndication 框架中(参阅第 13 章), title 和 description 的模板会自动访问变量 {{ site }} ,它其实是代表当前站点的 Site 对象。 而且,如果你不指定一个合格的domain的话,提供目录URL的钩子将会使用当前“Site”对象的domain。

  • 在权限框架中(参见十四章),视图django.contrib.auth.views.login把当前Site名字和对象分别以{{ site_name }}{{ site }}的形式传给了模板。

django book多站点学习的更多相关文章

  1. 【转】Django Model field reference学习总结

    Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...

  2. django book用户认证学习

    用户与Authentication 通过session,我们可以在多次浏览器请求中保持数据, 接下来的部分就是用session来处理用户登录了. 当然,不能仅凭用户的一面之词,我们就相信,所以我们需要 ...

  3. Django REST framework JWT学习

    1.JWT学习 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证.我们不再使用Session认证机制,而使用Json Web Token认证机制. Json web toke ...

  4. Django 1.7 Tutorial 学习笔记

    官方教程在这里 : Here 写在前面的废话:)) 以前学习新东西,第一想到的是找本入门教程,按照书上做一遍.现在看了各种网上的入门教程后,我觉得还是看官方Tutorial靠谱.书的弊端一说一大推 本 ...

  5. django中的站点管理

    所谓网页开发是有趣的,管理界面是千篇一律的.所以就有了django自动管理界面来减少重复劳动. 一.激活管理界面 1.django.contrib包 django自带了很多优秀的附加组件,它们都存在于 ...

  6. Django官方文档学习2——数据库及模板

    网址:https://docs.djangoproject.com/en/1.10/intro/tutorial02/ 1.扫描installed_apps,创建需要的数据库table python ...

  7. 构建高可用web站点学习--前言

    前言:本人对于提高web站点的访问量等的有很浓厚的兴趣,也学习了将近一年的时间,希望能总结点东西,虽然很多东西都是从书籍和资料中学习的,而不是原创,但是这是我总结的一点感悟和进行的分类吧.而且可能思路 ...

  8. 构建高可用web站点学习(二)

    web站点的缓存学习 缓存在web应用里面十分常见,也有各种各样的缓存,从请求开始一直到代码处理的阶段都可以采取缓存.下面就逐一介绍: 一.客户端缓存(浏览器和http方面) 前端页面缓存主要遵循ht ...

  9. Django:之Sitemap站点地图、通用视图和上下文渲染器

    Django中自带了sitemap框架,用来生成xml文件 Django sitemap演示: sitemap很重要,可以用来通知搜索引擎页面的地址,页面的重要性,帮助站点得到比较好的收录. 开启si ...

随机推荐

  1. RabbitMQ vhost 配置

    RabbitMQ vhost 配置 rabbitmqctl set_vhost_limits是用来定义虚拟主机限制的命令 配置最大连接限制 要限制vhost vhost_name中并发客户端连接的 总 ...

  2. 2016-2017 ACM-ICPC, Egyptian Collegiate Programming Contest(solved 8/11)

    这套题似乎是省选前做的,一直没来写题解---补上补上>_< 链接:http://codeforces.com/gym/101147 一样先放上惨不忍睹的成绩好了--- Problem A ...

  3. 生成DLL

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.R ...

  4. jquery 追加元素的方法(append prepend after before 的区别)

    append() 方法在被选元素的结尾插入内容. prepend() 方法在被选元素的开头插入内容. after() 方法在被选元素之后插入内容. before() 方法在被选元素之前插入内容. &l ...

  5. [HDU_3652]B-number

    题目描述 A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the ...

  6. Linux平台用C++实现事件对象,同步线程

    前文在Win32平台上用C++实现了事件对象Event,对线程进行同步,以达到期望目的.这次在Linux平台上实现与之类似的事件对象.与其相关的一组API包括:pthread_mutex_init,p ...

  7. mydumper备份

    1.安装工作 cd /usr/local/softwareyum install -y glib2-devel  zlib-devel pcre-develwget https://launchpad ...

  8. 【原创】Win7 IE故障:APPCRASH,d3d9.dll,c0000005

    问题 今天使用使用IE登录某网址,发现总是报错,如下图,无法浏览. 解决方案 主要讲IE的呈现方案修改即可,如下步骤: 在IE的[Internet选项]选择[高级]选项卡,在[加速的图形]中勾选[使用 ...

  9. 转载——为Xamarin更好的开发而改写的库

    本人现今一直奋战在Xamarin.Android,可能有人会疑惑Xamarin本身就是跨平台的,为什么不能直接跨IOS和Android,这个当然是最后的目标,只是现今你连Android都不能拿出符合商 ...

  10. Netty源码学习(七)FastThreadLocal

    0. FastThreadLocal简介 如同注释中所说:A special variant of ThreadLocal that yields higher access performance ...