Django integrated sub-framework

黄舟
Release: 2017-01-17 14:05:28
Original
1175 people have browsed it

Python has many advantages, one of which is the "ready-to-use" principle: when installing Python, a large number of standard software packages are installed, so that you can use it immediately without downloading it yourself. Django also follows this principle and also includes its own standard library. This chapter will talk about these integrated sub-frameworks.


Django standard library


Django’s standard library is placed in the django.contrib package. Each subpackage is an independent package of additional features. They are generally not necessarily related to each other, but some django.contrib sub-packages may depend on other packages.


There is no mandatory requirement for the type of functions in django.contrib. Some of these packages come with models (and thus require you to install the corresponding tables in your database), but others consist of standalone middleware and template tags.


The common features of the django.contrib development package are: even if you delete the entire django.contrib development package, you can still use the basic functions of
Django without encounter any problems. When Django developers add new functionality to the framework, they will strictly follow this doctrine when deciding whether to put the new functionality into django.contrib.


django.contrib consists of the following development packages:


§ admin: automated site management tool. Please see the Django admin site


#§ auth: Django’s user authentication framework. Please check Sessions, Users and Registration


§ comments: A comment application. Currently, this application is under intense development, so it cannot be given at the time of publication of this book. A complete description and more information about this application can be found on Django's official website.


§ contenttypes: This is a framework for document type hooks, each Installed Django modules as a standalone document type. This framework is primarily used within Django by other applications, and it is primarily intended for advanced Django developers. You can learn more about this framework by reading the source code. The source code is located at django/contrib/contenttypes/.


§ csrf: This module is used to prevent cross-site Request Forgery (CSRF). See the later section titled "CSRF Defense."


§ flatpages: A module for managing single HTML content in a database, see the section titled "Flatpages" below.


§ humanize: A series of Django
module filters used to increase the humanization of data.


§ markup: a series of Django
template filters used to implement some common markup languages.


§ redirects: A framework used to manage redirects.


§ sessions: Django's session framework, see Sessions, Users, and Registration.


§ sitemaps: Framework used to generate XML
files for site maps. See Django outputting non-HTML content.


§ sites: A framework that allows you to manage multiple websites within the same database and Django installation.


§ syndication: A framework for generating aggregated feeds using RSS
and Atom. See Django outputting non-HTML content.


This chapter will next describe in detail the contents of the django.contrib development package that have not been introduced before.


Multiple Sites


Django’s multisite system is a universal framework that allows you to Operate multiple websites under one database and the same Django project. This is an abstract concept that can be a bit difficult to understand, so let’s start with a few practical scenarios where it can come in handy.


Scenario 1: Reusing data for multiple sites




##As we said in Chapter 1, the websites LJWorld.com and Lawrance.com

built with Django are controlled by the same news organization: The Lawrence Journal in Lawrence, Kansas World Newspapers. LJWorld.com focuses on news, while
Lawrence.com focuses on local entertainment. Sometimes, however, an editor may need to publish an article to two websites.


A dead-end solution to this problem might be to use a different database for each site, and then ask the site maintainer to publish the same article twice: once for LJWorld .com and another time Lawrence.com. But this is inefficient for site administrators, and keeping multiple copies of the same article in the database is redundant.


A better solution? The two websites use the same article database and associate each article with one or more sites using a many-to-many relationship. The Django site framework provides a database that records which articles can be associated. It is a hook that associates data with one or more sites.


Scenario 2: Store your website name/domain in a unique location


LJWorld.com and Lawrence .com has an email reminder function, allowing readers to receive notifications immediately after news occurs after registering. This is a perfect mechanism: a reader submits a registration form and immediately receives a "Thank you for signing up" email.




It is obviously inefficient and redundant to implement the code of this registration process twice, so the two sites use the same code in the background. But the notification thanking you for signing up needs to be different in both websites. By using the Site object, we can extract the thank you notification by using the name of the current site (for example, 'LJWorld.com') and domain (for example, 'www.ljworld.com').


Django's multisite framework provides you with a place to store the name and domain of each site in your Django project, which means you can reuse them in the same way. these values.


How to use the multi-site framework


The multi-site framework is not so much a framework as it is a series of conventions . Everything is based on two simple concepts:


§ The Site model located in django.contrib.sites has two fields: domain and name.


#§ The SITE_ID setting specifies the database ID of the Site object associated with a specific configuration file.


#How you use these two concepts is up to you, but Django does it automatically through a few simple conventions.


To install a multi-site application, perform the following steps:


1. Change 'django.contrib.sites 'Add to INSTALLED_APPS.


2. Run the manage.py syncdb command to install the django_site table into the database.


3. Add one or more
'Site' objects through the Django management background or through the Python API. Create a Site object for each site (or domain) supported by the Django
project.


#4. Define a SITE_ID variable in each settings file. The value of this variable should be the database
ID of the Site object of the site supported by the configuration file.


Functions of the multi-site framework


The following sections describe several things that can be accomplished with the multi-site framework Work.


Data reuse across multiple sites


As explained in Scenario 1, to reuse data across multiple sites To reuse data between sites, you only need to add a many-to-many field for Site in the model, for example:

from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
sites = models.ManyToManyField(Site)
Copy after login

This is the basic step for performing article association operations for multiple sites in the database. Using this technique in place, you can reuse the same piece of Django view code across multiple sites. Continuing with the Article model example, here is a possible article_detail view:

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
Copy after login

# ...

This view method is reusable because it dynamically checks the articles site based on the value set by SITE_ID.


For example, LJWorld.coms settings file has a SITE_ID set to 1, and
Lawrence.coms settings file has a SITE_ID set to 2. If this view is called while LJWorld.coms is active, it will limit the search to articles whose site list includes
LJWorld.com.


Associating content with a single site


Similarly, you can also use foreign keys in many-to-one relationships Associate a model to the Site model.


For example, if a certain article can only appear on one site, you can use the following model:

from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
site = models.ForeignKey(Site)
Copy after login

This is different from the previous one are as useful as those introduced in the section.


Hooking the current site from the view


Under the hood, by using the multisite framework in a Django view, you You can have the view do different things depending on the calling site, for example:

from django.conf import settings
def my_view(request):
if settings.SITE_ID == 3:
# Do something.
else:
# Do something else.
Copy after login

Of course, hardcoding the site ID like that is ugly. A slightly simpler way to accomplish this is to view the current site domain:

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.
Copy after login

It is common to obtain the settings.SITE_ID value from the Site object, so the Site model manager
(Site.objects) has a get_current( )method. The following example is equivalent to the previous one:


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.
Copy after login

Note


In this final example, You don't need to import django.conf.settings.


Get the current domain for rendering


As explained in scenario two, for storing the site name and domain name For the DRY (Dont Repeat Yourself) method (store the site name and domain name in one location), you only need to reference the name and domain of the current Site object. For example:

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])
# ...
Copy after login

Continuing with the LJWorld.com and Lawrence.com
examples we are discussing, in Lawrence.com
the subject line of the email is "Thanks for signing up for the Lawrence.com reminder email." At
LJWorld.com, the subject line of the email is "Thanks for signing up for LJWorld.com reminder email." This site-related behavior also applies to email message subjects.


#A more flexible (but heavier-weight) way to accomplish this is to use Django's template system. Assuming that Lawrence.com and
LJWorld.com each have different template directories (TEMPLATE_DIRS), you can easily transfer the work to the template system as follows:

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])
# ...
Copy after login


In this example, you have to create a subject.txt and message.txt template in both the template directories of LJWorld.com and Lawrence.com
. As stated before, this approach brings more flexibility, but also more complexity.


Using as many Site objects as possible is a good way to reduce unnecessary complex and redundant work.


Get the full URL of the current domain


Django 的get_absolute_url()约定对与获取不带域名的对象 URL非常理想,但在某些情形下,你可能想显示某个对象带有http://和域名以及所有部分的完整
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())
Copy after login

'http://example.com/mymodel/objects/3/'

当前站点管理器


如果站点在你的应用中扮演很重要的角色,请考虑在你的模型中使用方便的CurrentSiteManager。这是一个模型管理器(见附录B),它会自动过滤使其只包含与当前站点相关联的对象。


通过显示地将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(maxlength=100)
pub_date = models.DateField()
site = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager()
Copy after login

通过该模型,``Photo.objects.all()``将返回数据库中所有的Photo对象,而Photo.on_site_all()仅根据SITE_ID设置返回与当前站点相关联的Photo对象。


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

Photo.objects.filter(site=settings.SITE_ID)
Copy after login

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(maxlength=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')
Copy after login

如果试图使用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框架中(参阅Django输出非HTML内容),title和description的模板自动访问变量{{
site }},它就是代表当前着桨的Site对象。Also, the hook for providing item URLs will use thedomainfrom
the currentSiteobject if you dont specify a fully qualified domain.


§ 在身份验证框架(参见Django会话、用户和注册)中,django.contrib.auth.views.login视图将当前Site名称作为{{
site_name }}传递给模板。


Flatpages - 简单页面


尽管通常情况下总是建造和运行数据库驱动的 Web应用,你还是会需要添加一两张一次性的静态页面,例如“关于”页面,或者“隐私策略”页面等等。可以用像
Apache 这样的标准Web服务器来处理这些静态页面,但却会给应用带来一些额外的复杂性,因为你必须操心怎么配置 Apache,还要设置权限让整个团队可以修改编辑这些文件,而且你还不能使用
Django 模板系统来统一这些页面的风格。


这个问题的解决方案是使用位于django.contrib.flatpages开发包中的 Django简单页面(flatpages)应用程序。该应用让你能够通过
Django超级管理站点来管理这些一次性的页面,还可以让你使用 Django模板系统指定它们使用哪个模板。它在后台使用了 Django模型,也就是说它将页面存放在数据库中,你也可以像对待其他数据一样用标准
Django数据库 API
存取简单页面。


简单页面以它们的 URL和站点为键值。当创建简单页面时,你指定它与哪个URL以及和哪个站点相关联。


使用简单页面


安装平页面应用程序必须按照下面的步骤:


1. 添加'django.contrib.flatpages'到INSTALLED_APPS设置。django.contrib.flatpages依赖于django.contrib.sites,所以确保这两个开发包都包括在``INSTALLED_APPS``设置中。


2. 将'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'添加到MIDDLEWARE_CLASSES设置中。


3. 运行manage.py syncdb命令在数据库中创建必需的两个表。


简单页面应用程序在数据库中创建两个表:django_flatpage和django_flatpage_sites。django_flatpage只是将
URL 映射到到标题和一段文本内容。django_flatpage_sites是一个多对多表,用于关联某个简单页面以及一个或多个站点。


该应用所带来的FlatPage模型在django/contrib/flatpages/models.py进行定义,如下所示:

from django.db import models
from django.contrib.sites.models import Site
class FlatPage(models.Model):
url = models.CharField(maxlength=100)
title = models.CharField(maxlength=200)
content = models.TextField()
enable_comments = models.BooleanField()
template_name = models.CharField(maxlength=70, blank=True)
registration_required = models.BooleanField()
sites = models.ManyToManyField(Site)
Copy after login

让我们逐项看看这些字段的含义:


§ url:该简单页面所处的 URL,不包括域名,但是包含前导斜杠
(例如/about/contact/)。


§ title:简单页面的标题。框架不对它作任何特殊处理。由你通过模板来显示它。


§ content:简单页面的内容 (即HTML
页面)。框架不会对它作任何特别处理。由你负责使用模板来显示。


§ enable_comments:是否允许该简单页面使用注释。框架不对此做任何特别处理。你可在模板中检查该值并根据需要显示注释窗体。


§ template_name:用来解析该简单页面的模板名称。这是一个可选项;如果未指定模板或该模板不存在,系统会退而使用默认模板flatpages/default.html。


§ registration_required:是否注册用户才能查看此简单页面。该设置项集成了 Djangos验证/用户框架。


§ sites:该简单页面放置的站点。该项设置集成了 Django多站点框架,该框架在本章的《多站点》一节中有所阐述。


你可以通过 Django超级管理界面或者 Django数据库 API
来创建平页面。要了解更多内容,请查阅《添加、修改和删除简单页面》一节。


一旦简单页面创建完成,FlatpageFallbackMiddleware将完成(剩下)所有的工作。每当 Django引发
404 错误,作为终极手段,该中间件将根据所请求的 URL检查平页面数据库。确切地说,它将使用所指定的 URL以及SITE_ID设置对应的站点
ID查找一个简单页面。


如果找到一个匹配项,它将载入该简单页面的模板(如果没有指定的话,将使用默认模板flatpages/default.html)。同时,它把一个简单的上下文变量——flatpage(一个简单页面对象)传递给模板。在模板解析过程中,它实际用的是RequestContext。


如果FlatpageFallbackMiddleware没有找到匹配项,该请求继续如常处理。


注意


该中间件仅在发生 404(页面未找到)错误时被激活,而不会在 500(服务器错误)或其他错误响应时被激活。还要注意的是必须考虑MIDDLEWARE_CLASSES的顺序问题。通常,你可以把FlatpageFallbackMiddleware放在列表最后,因为它是一种终极手段。


添加、修改和删除简单页面


可以用两种方式增加、变更或删除简单页面:


通过超级管理界面


如果已经激活了自动的 Django超级管理界面,你将会在超级管理页面的首页看到有个 Flatpages区域。你可以像编辑系统中其它对象那样编辑简单页面。


通过 Python API


前面已经提到,简单页面表现为django/contrib/flatpages/models.py中的标准 Django模型。因此,你可以通过
Django数据库 API
来存取简单页面对象,例如:

>>> from django.contrib.flatpages.models import FlatPage
>>> from django.contrib.sites.models import Site
>>> fp = FlatPage(
... url='/about/',
... title='About',
... content=&#39;<p>About this site...</p>&#39;,
... enable_comments=False,
... template_name=&#39;&#39;,
... registration_required=False,
... )
>>> fp.save()
>>> fp.sites.add(Site.objects.get(id=1))
>>> FlatPage.objects.get(url=&#39;/about/&#39;)
<FlatPage: /about/ -- About>
Copy after login

使用简单页面模板


缺省情况下,系统使用模板flatpages/default.html来解析简单页面,但你也可以通过设定FlatPage对象的template_name字段来覆盖特定简单页面的模板。


你必须自己创建flatpages/default.html模板。只需要在模板目录创建一个flatpages目录,并把default.html文件置于其中。


简单页面模板只接受有一个上下文变量——flatpage,也就是该简单页面对象。


以下是一个flatpages/default.html模板范例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>{{ flatpage.title }}</title>
</head>
<body>
{{ flatpage.content }}
</body>
</html>
Copy after login

重定向


通过将重定向存储在数据库中并将其视为 Django模型对象,Django
重定向框架让你能够轻松地管理它们。比如说,你可以通过重定向框架告诉Django,把任何指向/music/的请求重定向到/sections/arts/music/。当你需要在站点中移动一些东西时,这项功能就派上用场了——网站开发者应该穷尽一切办法避免出现坏链接。


使用重定向框架


安装重定向应用程序必须遵循以下步骤:


1. 将'django.contrib.redirects'添加到INSTALLED_APPS设置中。


2. 将'django.contrib.redirects.middleware.RedirectFallbackMiddleware'添加到MIDDLEWARE_CLASSES设置中。


3. 运行manage.py syncdb命令将所需的表安装到数据库中。


manage.py syncdb在数据库中创建了一个django_redirect表。这是一个简单的查询表,只有site_id、old_path和new_path三个字段。


你可以通过 Django超级管理界面或者 Django数据库 API
来创建重定向。要了解更多信息,请参阅《增加、变更和删除重定向》一节。


一旦创建了重定向,RedirectFallbackMiddleware类将完成所有的工作。每当 Django应用引发一个
404 错误,作为终极手段,该中间件将为所请求的 URL在重定向数据库中进行查找。确切地说,它将使用给定的old_path以及SITE_ID设置对应的站点
ID查找重定向设置。(查阅前面的《多站点》一节可了解关于SITE_ID和多站点框架的更多细节)然后,它将执行以下两个步骤:


§ 如果找到了匹配项,并且new_path非空,它将重定向到new_path。


§ 如果找到了匹配项,但new_path为空,它将发送一个 410 (Gone) HTTP头信息以及一个空(无内容)响应。


§ 如果未找到匹配项,该请求将如常处理。


该中间件仅为 404错误激活,而不会为 500
错误或其他任何状态码的响应所激活。


注意必须考虑MIDDLEWARE_CLASSES的顺序。通常,你可以将RedirectFallbackMiddleware放置在列表的最后,因为它是一种终极手段。


注意


如果同时使用重定向和简单页面回退中间件,必须考虑先检查其中的哪一个(重定向或简单页面)。我们建议将简单页面放在重定向之前(因此将简单页面中间件放置在重定向中间件之前),但你可能有不同想法。


增加、变更和删除重定向


你可以两种方式增加、变更和删除重定向:


通过超级管理界面


如果已经激活了全自动的 Django超级管理界面,你应该能够在超级管理首页看到重定向区域。可以像编辑系统中其它对象一样编辑重定向。


通过 Python API


django/contrib/redirects/models.py中的一个标准 Django模型代表了重定向。因此,你可以通过 Django数据库
API 来存取重定向对象,例如:

>>> from django.contrib.redirects.models import Redirect
>>> from django.contrib.sites.models import Site
>>> red = Redirect(
... site=Site.objects.get(id=1),
... old_path=&#39;/music/&#39;,
... new_path=&#39;/sections/arts/music/&#39;,
... )
>>> red.save()
>>> Redirect.objects.get(old_path=&#39;/music/&#39;)
<Redirect: /music/ ---> /sections/arts/music/>
Copy after login

CSRF 防护


django.contrib.csrf开发包能够防止遭受跨站请求伪造攻击 (CSRF).


CSRF, 又叫进程跳转,是一种网站安全攻击技术。当某个恶意网站在用户未察觉的情况下将其从一个已经通过身份验证的站点诱骗至一个新的 URL时,这种攻击就发生了,因此它可以利用用户已经通过身份验证的状态。开始的时候,要理解这种攻击技术比较困难,因此我们在本节将使用两个例子来说明。


一个简单的 CSRF例子


假定你已经登录到example.com的网页邮件账号。该网页邮件站点上有一个登出按钮指向了 URLexample.com/logout,换句话说,要登出的话,需要做的唯一动作就是访问
URL :example.com/logout。


通过在(恶意)网页上用隐藏一个指向 URLexample.com/logout的