先进的 Pyramid 设计特点¶
Pyramid 已经从头开始构建,以避免其他框架可能遇到的问题。
你不需要单身¶
你有没有尝试过参数化Django的 settings.py
同一个django应用程序的多个安装文件?您是否曾经需要对一个框架夹具进行猴子补丁,以使其在您的用例中正常工作?您是否尝试过使用异步服务器部署应用程序,但失败了?
所有这些问题都是 mutable global state 也称为 import time side effect 因使用 singleton 数据结构。
Pyramid 这样你就不会碰到这些问题。甚至可以运行 same Pyramid 应用程序在单个python进程中配置不同。这使跑步 Pyramid 在共享托管环境中,快照。
用谓词简化视图代码¶
有多少次您发现自己在视图代码逻辑的开头有这样的内容:
1if request.user.is_authenticated:
2 # do one thing
3else:
4 # do something else
与许多其他系统不同, Pyramid 允许您将多个视图与单个路由关联。例如,可以使用模式创建路线 /items
当路由匹配时,如果请求方法为get,则可以将请求发送到一个视图;如果请求方法为post,则可以发送到另一个视图,依此类推。
Pyramid 使用的系统 view predicate 允许这样做。匹配请求方法是使用 view predicate . 您还可以将视图与其他请求参数相关联,例如查询字符串中的元素、接受头、请求是否为Ajax(XHR)请求以及许多其他内容。
对于上面的示例,您可以这样做:
1@view_config(route_name="items", is_authenticated=True)
2def auth_view(request):
3 # do one thing
4
5@view_config(route_name="items")
6def anon_view(request):
7 # do something else
这种方法允许您开发更简单、更容易理解和更直接可测试的视图代码。
参见
也见 查看配置参数 .
不要担心交易¶
Pyramid 的 cookiecutter 呈现包含 交易管理 系统。当您使用这个系统时,您可以停止担心什么时候提交您的更改, Pyramid 为您处理。系统将在请求结束时提交,或者在出现异常时中止。
为什么这是好事?想象一下,在这种情况下,您手动提交对持久性层的更改。其他框架代码很可能会运行 之后 您的更改已完成。如果在另一个代码中发生错误,如果不十分小心,很容易得到不一致的数据。
使用事务管理可以避免您需要考虑这一点。请求成功完成,并且提交了所有更改,或者请求没有完成,所有更改都被中止。
Pyramid 的事务管理是可扩展的,因此您可以在多个数据库或不同类型的数据库之间同步提交。它还允许您在提交事务时有条件地发送电子邮件,否则保持安静。
参见
也见 sqlacalchemy+url调度wiki教程 (请注意,应用程序代码中没有commit语句)。
不要担心配置¶
当一个系统很小的时候,很容易把它都放在你的脑子里。但随着系统规模的扩大,配置变得更加复杂。您的应用程序可能会有成百上千的配置语句。
Pyramid 的配置系统跟踪您的每个语句。如果不小心添加了两个相同的,或 Pyramid 无法理解同时激活这两个语句意味着什么,它会在启动时大声抱怨。
Pyramid 不过,其配置系统并不愚蠢。如果您使用 include()
系统自动解决冲突。更多的本地语句比更少的本地语句更受欢迎。因此,您可以智能地将大系统分解为小系统。
参见
也见 冲突检测 .
从简单的部分组成强大的应用程序¶
说到 Pyramid 结构化的 include()
机制,它允许您从多个简单的python包组成复杂的应用程序。可以在主服务器中执行的所有配置语句 Pyramid 应用程序也可以在包含的包中使用。您可以添加视图、路由和订阅服务器,甚至可以设置身份验证和授权策略。
如果需要,可以扩展或重写现有应用程序的配置,方法是将其配置包含在自己的应用程序中,然后对其进行修改。
例如,如果您想要重用一个已有大量路由的现有应用程序,您只需使用 include
带有 route_prefix
. 该应用程序的所有路由都将可用,并按您的要求添加前缀:
1from pyramid.config import Configurator
2
3if __name__ == '__main__':
4 config = Configurator()
5 config.include('pyramid_jinja2')
6 config.include('pyramid_exclog')
7 config.include('some.other.package', route_prefix='/somethingelse')
参见
也见 包括来自外部源的配置 和 构建可扩展应用程序的规则 .
以您的方式验证用户¶
Pyramid 有一个强大的安全系统,可以为您量身定做。根据您的需要构建您自己的安全策略,或者使用所提供的众多帮助器中的一个来轻松实现常见的身份验证和授权模式。
参见
也见 编写安全策略 .
建设资源树¶
Pyramid 支架 traversal ,将URL映射到具体的 resource tree . 如果您的应用程序自然地由不同类型内容的任意层次结构(如CMS或文档管理系统)组成,则遍历适合您。如果您需要高粒度的安全模型(“Jane可以在 this 文件夹,但不是 that 1”),遍历可以是一种强大的方法。
使用Tweens对每个请求执行操作¶
Pyramid 有一个系统,用于对调用了 tween . 该系统在概念上与WSGi相似。 middleware ,但可能更有用,因为 tween 运行在 Pyramid 并且可以访问模板、请求对象和其他细节。
这个 Pyramid 调试工具栏是 tween ,就像是 pyramid_tm
事务管理器。
参见
也见 登记吐温 .
从视图中返回所需内容¶
我们已经在其他地方(在 Pyramid 介绍 )如何使用 renderer 允许您从视图代码返回简单的python字典。但有些框架允许您从视图可调用文件返回字符串或元组。当框架允许这样做时,代码看起来稍微漂亮一点,因为导入和代码更少。例如,比较:
1from pyramid.response import Response
2
3def aview(request):
4 return Response("Hello world!")
对此:
1def aview(request):
2 return "Hello world!"
更好看,对吧?
走出盒子, Pyramid 如果尝试运行上面的第二个示例,将引发异常。毕竟,视图应该返回一个响应,“显式优于隐式”。
但是如果你是一个喜欢简单美学的开发者, Pyramid 提供了一种方法来支持这类事情, response adapter :
1from pyramid.config import Configurator
2from pyramid.response import Response
3
4def string_response_adapter(s):
5 response = Response(s)
6 response.content_type = 'text/html'
7 return response
在配置中注册了新的响应适配器:
1if __name__ == '__main__':
2 config = Configurator()
3 config.add_response_adapter(string_response_adapter, str)
这样,您就可以从任何视图可调用文件返回字符串,例如:
1def helloview(request):
2 return "Hello world!"
3
4def goodbyeview(request):
5 return "Goodbye world!"
你甚至可以使用 response adapter 要允许自定义内容类型和返回代码:
1from pyramid.config import Configurator
2
3def tuple_response_adapter(val):
4 status_int, content_type, body = val
5 response = Response(body)
6 response.content_type = content_type
7 response.status_int = status_int
8 return response
9
10def string_response_adapter(body):
11 response = Response(body)
12 response.content_type = 'text/html'
13 response.status_int = 200
14 return response
15
16if __name__ == '__main__':
17 config = Configurator()
18 config.add_response_adapter(string_response_adapter, str)
19 config.add_response_adapter(tuple_response_adapter, tuple)
有了这个,这两个视图都将按预期工作:
1def aview(request):
2 return "Hello world!"
3
4def anotherview(request):
5 return (403, 'text/plain', "Forbidden")
参见
也见 更改金字塔处理视图响应的方式 .
使用全局响应对象¶
视图必须返回响应。但是在视图代码中构建它们是一件很麻烦的事情。也许注册一个 response adapter 如上图所示,工作太多了。 Pyramid 还提供全局响应对象。如果您愿意,可以直接使用它:
1def aview(request):
2 response = request.response
3 response.body = 'Hello world!'
4 response.content_type = 'text/plain'
5 return response
参见
也见 呈现响应的不同属性 .
扩展配置¶
也许 Pyramid 配置程序的语法对您来说有点冗长。或者,您可能希望在不要求核心开发人员更改的情况下向配置中添加特性 Pyramid 本身?
您可以扩展 Pyramid 的 configurator 有你自己的指示。例如,假设你发现自己在打电话 pyramid.config.Configurator.add_view()
重复地通常,您可以通过现有的快捷方式来摆脱无聊,但假设没有这样的快捷方式:
1from pyramid.config import Configurator
2
3config = Configurator()
4config.add_route('xhr_route', '/xhr/{id}')
5config.add_view('my.package.GET_view', route_name='xhr_route', xhr=True,
6 permission='view', request_method='GET')
7config.add_view('my.package.POST_view', route_name='xhr_route', xhr=True,
8 permission='view', request_method='POST')
9config.add_view('my.package.HEAD_view', route_name='xhr_route', xhr=True,
10 permission='view', request_method='HEAD')
相当乏味,对吧?您可以将指令添加到 Pyramid configurator 要使某些单调的工作自动化,请执行以下操作:
1from pyramid.config import Configurator
2
3def add_protected_xhr_views(config, module):
4 module = config.maybe_dotted(module)
5 for method in ('GET', 'POST', 'HEAD'):
6 view = getattr(module, 'xhr_%s_view' % method, None)
7 if view is not None:
8 config.add_view(view, route_name='xhr_route', xhr=True,
9 permission='view', request_method=method)
10
11config = Configurator()
12config.add_directive('add_protected_xhr_views', add_protected_xhr_views)
完成后,可以调用刚刚添加的指令作为 configurator 对象:
1config.add_route('xhr_route', '/xhr/{id}')
2config.add_protected_xhr_views('my.package')
好多了!
您也可以与其他人共享您的配置代码。将代码添加到python包中。打电话给 add_directive()
在一个函数中。当其他程序员安装您的包时,他们可以通过将函数传递给 include()
.
参见
反省你的申请¶
如果您正在构建一个大型的可插拔系统,那么能够获得一个已插入内容的列表是很有用的。 在应用程序运行时 . 例如,您可能希望根据用户注册的视图列表,在屏幕顶部向用户显示一组选项卡。
Pyramid 提供了一个 introspector 就为了这个目的。
下面是一个使用 Pyramid 的 introspector 从视图中:
1from pyramid.view import view_config
2from pyramid.response import Response
3
4@view_config(route_name='bar')
5def show_current_route_pattern(request):
6 introspector = request.registry.introspector
7 route_name = request.matched_route.name
8 route_intr = introspector.get('routes', route_name)
9 return Response(str(route_intr['pattern']))
参见
也见 金字塔形自省 .