定义视图¶
A view callable 在一个 Pyramid 应用程序通常是一个简单的python函数,它接受一个名为 request . 假定视图可调用返回 response 对象。
请求对象有一个字典作为名为 matchdict
. 一 matchdict
映射匹配URL中的占位符 pattern
到中路径的子字符串 request 网址。例如,如果 pyramid.config.Configurator.add_route()
有模式 /{{one}}/{{two}}
和用户访问 http://example.com/foo/bar
,我们的模式将与 /foo/bar
以及 matchdict
看起来像 {{'one':'foo', 'two':'bar'}}
.
添加 docutils
依赖项¶
请记住,在上一章中,我们添加了 bcrypt
包。同样,我们的应用程序中的视图代码将依赖于一个不依赖于原始“教程”应用程序的包。
我们需要在 docutils
包装给我们 tutorial
包装的 setup.py
通过将此依赖项分配给 requires
名单。
正常开放 tutorial/setup.py
并编辑如下:
11requires = [
12 'alembic',
13 'bcrypt',
14 'docutils',
15 'plaster_pastedeploy',
16 'pyramid',
17 'pyramid_debugtoolbar',
18 'pyramid_jinja2',
19 'pyramid_retry',
20 'pyramid_tm',
21 'SQLAlchemy',
22 'transaction',
23 'waitress',
24 'zope.sqlalchemy',
25]
只需添加突出显示的行。
同样,正如我们在上一章中所做的,依赖项现在需要安装,因此重新运行 $VENV/bin/pip install -e .
命令。
Static Assets¶
我们的模板命名Static Assets,包括CSS和图像。我们不需要在包中创建这些文件 static
目录,因为它们是在我们创建项目时提供的。
例如,CSS文件将通过 http://localhost:6543/static/theme.css
凭借对 add_static_view
我们在 tutorial/routes.py
文件。任何数量和类型的静态资产都可以放在这个目录(或子目录)中,并且只是通过URL或使用便利方法引用的。 static_url
,例如, request.static_url('<package>:static/foo.css')
在模板中。
将路由添加到 routes.py
¶
这就是 URL Dispatch 教程,让我们从向我们的应用程序添加一些URL模式开始。稍后我们将附加视图来处理URL。
这个 tutorial/routes.py
文件包含 pyramid.config.Configurator.add_route()
用于向应用程序添加路由的调用。首先,我们将去掉模板使用名称创建的现有路由。 'home'
. 这只是一个例子,与我们的应用程序无关。
然后我们需要在 add_route
. 请注意 排序 这些声明中的一个非常重要。路由声明按注册顺序匹配。
添加映射模式的声明
/
(表示根URL)到名为的路由view_wiki
. 在下一步中,我们将把它映射到view_wiki
查看可调用的@view_config
附加到view_wiki
视图功能,依次由route_name='view_wiki'
.添加映射模式的声明
/{{pagename}}
到指定的路线view_page
. 这是页面的常规视图。同样,在下一步中,我们将把它映射到view_page
查看可调用的@view_config
附加到view_page
视图功能,依次由route_name='view_page'
.添加映射模式的声明
/add_page/{{pagename}}
到指定的路线add_page
. 这是新页面的添加视图。我们会把它映射到我们的add_page
查看可调用的@view_config
附加到add_page
视图功能,依次由route_name='add_page'
.添加映射模式的声明
/{{pagename}}/edit_page
到指定的路线edit_page
. 这是页面的编辑视图。我们会把它映射到我们的edit_page
查看可调用的@view_config
附加到edit_page
视图功能,依次由route_name='edit_page'
.
由于我们的编辑, tutorial/routes.py
文件应如下所示:
1def includeme(config):
2 config.add_static_view('static', 'static', cache_max_age=3600)
3 config.add_route('view_wiki', '/')
4 config.add_route('view_page', '/{pagename}')
5 config.add_route('add_page', '/add_page/{pagename}')
6 config.add_route('edit_page', '/{pagename}/edit_page')
突出显示的行是需要添加或编辑的行。
警告
路线的顺序很重要!如果你放置 /{{pagename}}/edit_page
before /add_page/{{pagename}}
, then we would never be able to add pages. This is because the first route would always match a request to /add_page/edit_page
whereas we want /add_page/..
有优先权。在这个特定的应用程序中,这并不是一个大问题,因为wiki页面总是像骆驼一样,但是在你自己的应用程序中注意到这种行为是很重要的。
CSRF保护¶
当处理改变数据库中数据的HTML表单时,我们需要验证表单提交是否合法,而不是来自嵌入在第三方网站中的URL。这是通过在每个表单中添加第三方无法轻易猜到的唯一令牌来完成的。有关CSRF的更多信息,请访问 防止跨站点请求伪造攻击 . 在本教程中,我们将在cookie中存储活动CSRF令牌。
让我们添加一个新的 tutorial/security.py
文件:
1from pyramid.csrf import CookieCSRFStoragePolicy
2
3
4def includeme(config):
5 config.set_csrf_storage_policy(CookieCSRFStoragePolicy())
6 config.set_default_csrf_options(require_csrf=True)
因为我们添加了一个新的 tutorial/security.py
模块,我们需要包括它。打开文件 tutorial/__init__.py
并编辑以下行:
1from pyramid.config import Configurator
2
3
4def main(global_config, **settings):
5 """ This function returns a Pyramid WSGI application.
6 """
7 with Configurator(settings=settings) as config:
8 config.include('pyramid_jinja2')
9 config.include('.security')
10 config.include('.routes')
11 config.include('.models')
12 config.scan()
13 return config.make_wsgi_app()
对于改变数据的表单,我们将确保将CSRF令牌添加到表单中,使用 pyramid.csrf.get_csrf_token()
.
在中添加视图函数 views/default.py
¶
是时候进行重大变革了。正常开放 tutorial/views/default.py
并替换为以下内容:
1from docutils.core import publish_parts
2from html import escape
3from pyramid.httpexceptions import (
4 HTTPNotFound,
5 HTTPSeeOther,
6)
7from pyramid.view import view_config
8import re
9
10from .. import models
11
12
13# regular expression used to find WikiWords
14wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
15
16@view_config(route_name='view_wiki')
17def view_wiki(request):
18 next_url = request.route_url('view_page', pagename='FrontPage')
19 return HTTPSeeOther(location=next_url)
20
21@view_config(route_name='view_page', renderer='tutorial:templates/view.jinja2')
22def view_page(request):
23 pagename = request.matchdict['pagename']
24 page = request.dbsession.query(models.Page).filter_by(name=pagename).first()
25 if page is None:
26 raise HTTPNotFound('No such page')
27
28 def add_link(match):
29 word = match.group(1)
30 exists = request.dbsession.query(models.Page).filter_by(name=word).all()
31 if exists:
32 view_url = request.route_url('view_page', pagename=word)
33 return '<a href="%s">%s</a>' % (view_url, escape(word))
34 else:
35 add_url = request.route_url('add_page', pagename=word)
36 return '<a href="%s">%s</a>' % (add_url, escape(word))
37
38 content = publish_parts(page.data, writer_name='html')['html_body']
39 content = wikiwords.sub(add_link, content)
40 edit_url = request.route_url('edit_page', pagename=page.name)
41 return dict(page=page, content=content, edit_url=edit_url)
42
43@view_config(route_name='edit_page', renderer='tutorial:templates/edit.jinja2')
44def edit_page(request):
45 pagename = request.matchdict['pagename']
46 page = request.dbsession.query(models.Page).filter_by(name=pagename).one()
47 if request.method == 'POST':
48 page.data = request.params['body']
49 next_url = request.route_url('view_page', pagename=page.name)
50 return HTTPSeeOther(location=next_url)
51 return dict(
52 pagename=page.name,
53 pagedata=page.data,
54 save_url=request.route_url('edit_page', pagename=page.name),
55 )
56
57@view_config(route_name='add_page', renderer='tutorial:templates/edit.jinja2')
58def add_page(request):
59 pagename = request.matchdict['pagename']
60 if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0:
61 next_url = request.route_url('edit_page', pagename=pagename)
62 return HTTPSeeOther(location=next_url)
63 if request.method == 'POST':
64 body = request.params['body']
65 page = models.Page(name=pagename, data=body)
66 page.creator = (
67 request.dbsession.query(models.User).filter_by(name='editor').one())
68 request.dbsession.add(page)
69 next_url = request.route_url('view_page', pagename=pagename)
70 return HTTPSeeOther(location=next_url)
71 save_url = request.route_url('add_page', pagename=pagename)
72 return dict(pagename=pagename, pagedata='', save_url=save_url)
我们添加了一些导入,并创建了一个正则表达式来查找“wikiwords”。
我们摆脱了 my_view
视图函数及其修饰符,在我们选择 sqlalchemy
CookiCutter中的后端选项。这只是一个例子,与我们的应用程序无关。我们还删除了 db_err_msg
字符串。
然后我们加了四个 view callable 我们的功能 tutorial/views/default.py
模块,如前一步所述:
view_wiki()
-显示wiki本身。它将在根URL上应答。view_page()
-显示单个页面。edit_page()
-允许用户编辑页面。add_page()
-允许用户添加页面。
我们将在下面的部分中对每一个进行简要描述。
备注
文件名没有什么特别之处 default.py
但它是一个python模块。在任意命名的模块中,一个项目的代码库中可能有许多视图可调用文件。实现视图可调用文件的模块通常 view
以它们的名义(或者可能位于名为的应用程序包的python子包中) views
但这只是惯例,不是要求。
这个 view_wiki
视图函数¶
下面是 view_wiki
视图函数及其修饰器:
16@view_config(route_name='view_wiki')
17def view_wiki(request):
18 next_url = request.route_url('view_page', pagename='FrontPage')
19 return HTTPSeeOther(location=next_url)
view_wiki()
是 default view 当对wiki的根URL发出请求时会调用它。它总是重定向到表示“FrontPage”路径的URL。
这个 view_wiki
View Callable始终重定向到名为“FrontPage”的网页资源的URL。为此,它返回 pyramid.httpexceptions.HTTPSeeOther
类(其实例实现 pyramid.interfaces.IResponse
界面,像 pyramid.response.Response
)它使用 pyramid.request.Request.route_url()
用于构造 FrontPage
页面(即 http://localhost:6543/FrontPage
,并将其用作 HTTPSeeOther
响应,形成HTTP重定向。
这个 view_page
视图函数¶
这是密码 view_page
视图函数及其修饰器:
21@view_config(route_name='view_page', renderer='tutorial:templates/view.jinja2')
22def view_page(request):
23 pagename = request.matchdict['pagename']
24 page = request.dbsession.query(models.Page).filter_by(name=pagename).first()
25 if page is None:
26 raise HTTPNotFound('No such page')
27
28 def add_link(match):
29 word = match.group(1)
30 exists = request.dbsession.query(models.Page).filter_by(name=word).all()
31 if exists:
32 view_url = request.route_url('view_page', pagename=word)
33 return '<a href="%s">%s</a>' % (view_url, escape(word))
34 else:
35 add_url = request.route_url('add_page', pagename=word)
36 return '<a href="%s">%s</a>' % (add_url, escape(word))
37
38 content = publish_parts(page.data, writer_name='html')['html_body']
39 content = wikiwords.sub(add_link, content)
40 edit_url = request.route_url('edit_page', pagename=page.name)
41 return dict(page=page, content=content, edit_url=edit_url)
view_page()
用于显示wiki的单个页面。它渲染了 reStructuredText 页面正文(存储为 data
的属性 Page
模型对象)作为HTML。然后它用HTML锚替换每个锚 WikiWord 使用已编译的正则表达式在呈现的HTML中引用。
名为 add_link
用作 wikiwords.sub
,表示应该调用它为内容中找到的每个wikiword匹配项提供一个值。如果wiki已经包含一个具有匹配wikiword名称的页面, add_link()
生成一个视图链接,用作替换值并返回它。如果wiki不包含具有匹配wikiword名称的网页, add_link()
生成一个“添加”链接作为替换值并返回它。
因此, content
变量现在是一个完整的HTML格式,包含各种视图,并根据当前页面对象的内容为wikiwords添加链接。
然后,我们生成一个编辑URL,因为在这里比在模板中更容易执行,并且我们返回一个带有许多参数的字典。事实是 view_page()
返回字典(与 response 对象)是指向 Pyramid 它应该尝试使用 renderer 与呈现响应的视图配置关联。在我们的例子中,使用的渲染器将是 view.jinja2
模板,如 @view_config
应用于的装饰器 view_page()
.
如果页面不存在,那么我们需要通过提升 pyramid.httpexceptions.HTTPNotFound
触发404处理,定义见 tutorial/views/notfound.py
.
备注
使用 raise
对战 return
除了HTTP例外,这是一个很重要的区别,通常会把人搞得一团糟。在 tutorial/views/notfound.py
有一个 exception view 注册处理 HTTPNotFound
例外。只有引发的异常才会触发异常视图。如果 HTTPNotFound
返回,然后它有一个内部的“stock”模板,将使用该模板将自己呈现为响应。如果您没有看到异常视图被执行,这很可能是问题所在!见 在视图可调用文件中使用特殊异常 有关异常视图的详细信息。
这个 edit_page
视图函数¶
这是密码 edit_page
视图函数及其修饰器:
43@view_config(route_name='edit_page', renderer='tutorial:templates/edit.jinja2')
44def edit_page(request):
45 pagename = request.matchdict['pagename']
46 page = request.dbsession.query(models.Page).filter_by(name=pagename).one()
47 if request.method == 'POST':
48 page.data = request.params['body']
49 next_url = request.route_url('view_page', pagename=page.name)
50 return HTTPSeeOther(location=next_url)
51 return dict(
52 pagename=page.name,
53 pagedata=page.data,
54 save_url=request.route_url('edit_page', pagename=page.name),
55 )
edit_page()
当用户单击视图窗体上的“编辑此页”按钮时调用。它呈现一个编辑表单,但它也充当它所呈现表单的处理程序。这个 matchdict
传递给的请求的属性 edit_page
视图将具有 'pagename'
与用户要编辑的页面名称匹配的键。
如果视图执行 is 提交表格的结果(即。, request.method == 'POST'
)视图抓取 body
元素,并将其设置为 data
页对象的属性。然后重定向到 view_page
维基页面的视图。
如果视图执行是 not 表单提交的结果(即表达式 request.method != 'POST'
,视图只呈现编辑表单,传递页面对象和 save_url
将用作生成表单的操作。
备注
自从我们 request.dbsession
上一章中的定义是在 pyramid_tm
事务管理器,我们对会话管理的对象所做的任何更改都将自动提交。如果出现错误(甚至在稍后的模板代码中),更改将被中止。这意味着视图本身不需要关注提交/回滚逻辑。
这个 add_page
视图函数¶
这是密码 add_page
视图函数及其修饰器:
57@view_config(route_name='add_page', renderer='tutorial:templates/edit.jinja2')
58def add_page(request):
59 pagename = request.matchdict['pagename']
60 if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0:
61 next_url = request.route_url('edit_page', pagename=pagename)
62 return HTTPSeeOther(location=next_url)
63 if request.method == 'POST':
64 body = request.params['body']
65 page = models.Page(name=pagename, data=body)
66 page.creator = (
67 request.dbsession.query(models.User).filter_by(name='editor').one())
68 request.dbsession.add(page)
69 next_url = request.route_url('view_page', pagename=pagename)
70 return HTTPSeeOther(location=next_url)
71 save_url = request.route_url('add_page', pagename=pagename)
72 return dict(pagename=pagename, pagedata='', save_url=save_url)
add_page()
当用户单击 WikiWord 它还没有在系统中表示为一个页面。这个 add_link
中的函数 view_page
视图生成指向此视图的URL。 add_page()
还充当要添加页面对象时生成的表单的处理程序。这个 matchdict
传递给的请求的属性 add_page()
视图将具有构造URL和查找模型对象所需的值。
这个 matchdict
将有一个 'pagename'
与要添加的页的名称匹配的键。例如,如果通过调用添加视图, http://localhost:6543/add_page/SomeName
的价值 'pagename'
在 matchdict
将 'SomeName'
.
接下来执行检查以确定 Page
数据库中已存在。如果它已经存在,则客户端将重定向到 edit_page
视图,否则我们继续下一个检查。
如果视图执行 is 表单提交的结果(即表达式 request.method == 'POST'
,我们从表单数据中获取页面主体,创建一个具有此页面主体和取自的名称的页面对象。 matchdict['pagename']
,并使用将其保存到数据库中 request.dbession.add
. 由于我们还没有涉及身份验证,因此我们没有登录用户可以添加为页面的 creator
. 在我们到达教程中的那个点之前,我们假设所有页面都是由 editor
用户。因此,我们查询该对象,并将其设置为 page.creator
. 最后,我们将客户机重定向回 view_page
新创建页面的视图。
如果视图执行是 not 表单提交的结果(即表达式 request.method != 'POST'
是 False
)视图可调用呈现模板。为此,它生成一个 save_url
模板在呈现期间用作表单发布URL。我们在这里很懒惰,所以我们将使用相同的模板 (templates/edit.jinja2
)对于添加视图和页面编辑视图。为此,我们创建一个虚拟对象 Page
对象以满足编辑窗体的 some 页面对象公开为 page
. Pyramid 将与此视图关联的模板呈现为响应。
添加模板¶
这个 view_page
, add_page
和 edit_page
我们添加了引用A的视图 template . 每个模板都是 Jinja2 模板。这些模板将位于 templates
教程包的目录。Jinja2模板必须具有 .jinja2
延期应视为延期。
这个 layout.jinja2
模板¶
更新 tutorial/templates/layout.jinja2
内容如下,以强调的线条表示:
1<!DOCTYPE html>
2<html lang="{{request.locale_name}}">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta name="description" content="pyramid web application">
8 <meta name="author" content="Pylons Project">
9 <link rel="shortcut icon" href="{{request.static_url('tutorial:static/pyramid-16x16.png')}}">
10
11 <title>{% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
12
13 <!-- Bootstrap core CSS -->
14 <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
15
16 <!-- Custom styles for this scaffold -->
17 <link href="{{request.static_url('tutorial:static/theme.css')}}" rel="stylesheet">
18
19 <!-- HTML5 shiv and Respond.js IE8 support of HTML5 elements and media queries -->
20 <!--[if lt IE 9]>
21 <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
22 <script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js" integrity="sha384-ZoaMbDF+4LeFxg6WdScQ9nnR1QC2MIRxA1O9KWEXQwns1G8UNyIEZIQidzb0T1fo" crossorigin="anonymous"></script>
23 <![endif]-->
24 </head>
25
26 <body>
27
28 <div class="starter-template">
29 <div class="container">
30 <div class="row">
31 <div class="col-md-2">
32 <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png') }}" alt="pyramid web framework">
33 </div>
34 <div class="col-md-10">
35 <div class="content">
36 {% block content %}{% endblock %}
37 </div>
38 </div>
39 </div>
40 <div class="row">
41 <div class="links">
42 <ul>
43 <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
44 <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://web.libera.chat/#pyramid">IRC Channel</a></li>
45 <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
46 </ul>
47 </div>
48 </div>
49 <div class="row">
50 <div class="copyright">
51 Copyright © Pylons Project
52 </div>
53 </div>
54 </div>
55 </div>
56
57
58 <!-- Bootstrap core JavaScript
59 ================================================== -->
60 <!-- Placed at the end of the document so the pages load faster -->
61 <script src="//code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
62 <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
63 </body>
64</html>
因为我们使用的是模板引擎,所以我们可以将页面模板中的公共样板文件分解为可重用的组件。这样做的一个方法是通过块继承模板。
我们在布局模板中定义了两个占位符,其中子模板可以覆盖内容。这些块被命名为
subtitle
(第11行)content
(第36行)。请参阅 Jinja2 documentation 有关模板继承的详细信息。
这个 view.jinja2
模板¶
创造 tutorial/templates/view.jinja2
并增加以下内容:
1{% extends 'layout.jinja2' %}
2
3{% block subtitle %}{{page.name}} - {% endblock subtitle %}
4
5{% block content %}
6<p>{{ content|safe }}</p>
7<p>
8<a href="{{ edit_url }}">
9 Edit this page
10</a>
11</p>
12<p>
13 Viewing <strong>{{page.name}}</strong>, created by <strong>{{page.creator.name}}</strong>.
14</p>
15<p>You can return to the
16<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
17</p>
18{% endblock content %}
此模板由使用 view_page()
用于显示单个wiki页面。
我们从扩展
layout.jinja2
上面定义的模板,它提供了页面的框架(第1行)。我们推翻了
subtitle
从基本布局中阻止,将页面名称插入页面标题(第3行)。我们推翻了
content
从基本布局阻止将标记插入正文(第5-18行)。我们使用的变量被替换为
content
视图提供的值(第6行)。content
包含HTML,因此|safe
过滤器用于防止其转义(例如,将“>”更改为“>;”)。我们创建一个指向“编辑”URL的链接,当单击该链接时,将调用
edit_page
所请求页面的视图(第8-10行)。
这个 edit.jinja2
模板¶
创造 tutorial/templates/edit.jinja2
并增加以下内容:
1{% extends 'layout.jinja2' %}
2
3{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %}
4
5{% block content %}
6<p>
7Editing <strong>{{pagename}}</strong>
8</p>
9<p>You can return to the
10<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
11</p>
12<form action="{{ save_url }}" method="post">
13<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
14<div class="form-group">
15 <textarea class="form-control" name="body" rows="10" cols="60">{{ pagedata }}</textarea>
16</div>
17<div class="form-group">
18 <button type="submit" class="btn btn-default">Save</button>
19</div>
20</form>
21{% endblock content %}
此模板服务于两个用例。它被使用 add_page()
和 edit_page()
用于添加和编辑wiki页面。它显示一个包含表单的页面,并提供以下内容:
我们再次扩展
layout.jinja2
模板,提供页面的框架(第1行)。重写
subtitle
阻止以影响<title>
标签head
第页(第3行)。将CSRF令牌添加到表单中(第13行)。如果没有此行,尝试编辑页面将导致
400 Bad Request
错误。10行乘60列
textarea
字段名body
它在呈现时被任何现有的页面数据填充(第15行)。提交按钮(第18行)。
表单将回发到
save_url
视图提供的参数(第12行)。视图将使用body
价值。
这个 404.jinja2
模板¶
替换 tutorial/templates/404.jinja2
with the following content:
1{% extends "layout.jinja2" %}
2
3{% block content %}
4<h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
5<p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
6{% endblock content %}
此模板链接自 notfound_view
定义在 tutorial/views/notfound.py
如图所示:
1from pyramid.view import notfound_view_config
2
3
4@notfound_view_config(renderer='tutorial:templates/404.jinja2')
5def notfound_view(request):
6 request.response.status = 404
7 return {}
关于此配置,需要注意以下几点:
这个
notfound_view
在上面的代码片段中,称为 exception view . 有关详细信息,请参阅 在视图可调用文件中使用特殊异常 .这个
notfound_view
将响应状态设置为404。可以通过以下方式影响渲染器使用的响应对象: 呈现响应的不同属性 .这个
notfound_view
注册为异常视图并将被调用 only 如果pyramid.httpexceptions.HTTPNotFound
作为异常引发。这意味着对于通常从视图返回的任何响应都不会调用它。例如,在第27行tutorial/views/default.py
引发将触发视图的异常。
最后,我们可以删除 tutorial/templates/mytemplate.jinja2
通过选择的后端选项提供的模板 sqlalchemy
,因为我们已经为wiki创建了自己的模板。
备注
我们的模板使用 request
对象,我们的教程视图都没有返回到它们的字典中。 request
是使用模板呈现器时模板中“默认”可用的几个名称之一。见 渲染期间使用的系统值 有关将模板用作渲染器时默认情况下可用的其他名称的信息。
在浏览器中查看应用程序¶
我们最终可以在浏览器中检查我们的应用程序(请参见 启动应用程序 )启动浏览器并访问以下每个URL,检查结果是否符合预期:
http://localhost:6543/ invokes the
view_wiki
查看。这总是重定向到view_page
视图FrontPage
页面对象。http://localhost:6543/FrontPage invokes the
view_page
视图FrontPage
页面对象。http://localhost:6543/FrontPage/edit_page invokes the
edit_page
查看FrontPage
页面对象。http://localhost:6543/add_page/SomePageName invokes the
add_page
查看页面。如果页面已经存在,则它将用户重定向到edit_page
页面对象的视图。http://localhost:6543/SomePageName/edit_page invokes the
edit_page
查看现有页,或在该页不存在时生成错误。要生成错误,请访问http://localhost:6543/foobars/edit_页面,该页面将生成
NoResultFound: No row was found for one()
错误。您将看到由 pyramid_debugtoolbar .