15:更多视图类¶
将视图分组为类、共享配置、状态和逻辑。
背景¶
作为帮助构建更雄心勃勃的Web应用程序的使命的一部分,Pyramid为视图和视图类提供了更多的功能。
Pyramid文档讨论了作为Python“可调用”的视图。此可调用项可以是函数,也可以是具有 __call__
或者是python类。在最后一种情况下,类上的方法可以用 @view_config
将类方法注册到 configurator 作为一种观点。
起初,我们的观点是简单的、独立的功能。许多时候,您的视图是相关的:查看同一数据或处理同一数据的不同方法,或者处理多个操作的RESTAPI。将这些分组为 view class 有道理:
组视图。
集中一些重复的默认值。
分享一些州和帮手。
Pyramid视图 view predicates 根据诸如请求方法、表单参数等因素确定与请求匹配的视图。这些谓词提供了许多灵活性轴。
下面是一个包含四个操作的简单示例:查看指向表单的主页,保存更改,然后按“删除”按钮。
目标¶
将相关视图分组到视图类中。
使用类级别集中配置
@view_defaults
.根据请求数据向多个视图发送一个路由/url。
通过视图类在视图和模板之间共享状态和逻辑。
步骤¶
首先我们复制
templating
步骤:cd ..; cp -r templating more_view_classes; cd more_view_classes $VENV/bin/pip install -e .
我们的进路
more_view_classes/tutorial/__init__.py
需要一些替换模式:1from pyramid.config import Configurator 2 3 4def main(global_config, **settings): 5 config = Configurator(settings=settings) 6 config.include('pyramid_chameleon') 7 config.add_route('home', '/') 8 config.add_route('hello', '/howdy/{first}/{last}') 9 config.scan('.views') 10 return config.make_wsgi_app()
我们的
more_view_classes/tutorial/views.py
现在有一个包含多个视图的视图类:1from pyramid.view import ( 2 view_config, 3 view_defaults 4 ) 5 6 7@view_defaults(route_name='hello') 8class TutorialViews: 9 def __init__(self, request): 10 self.request = request 11 self.view_name = 'TutorialViews' 12 13 @property 14 def full_name(self): 15 first = self.request.matchdict['first'] 16 last = self.request.matchdict['last'] 17 return first + ' ' + last 18 19 @view_config(route_name='home', renderer='home.pt') 20 def home(self): 21 return {'page_title': 'Home View'} 22 23 # Retrieving /howdy/first/last the first time 24 @view_config(renderer='hello.pt') 25 def hello(self): 26 return {'page_title': 'Hello View'} 27 28 # Posting to /howdy/first/last via the "Edit" submit button 29 @view_config(request_method='POST', renderer='edit.pt') 30 def edit(self): 31 new_name = self.request.params['new_name'] 32 return {'page_title': 'Edit View', 'new_name': new_name} 33 34 # Posting to /howdy/first/last via the "Delete" submit button 35 @view_config(request_method='POST', request_param='form.delete', 36 renderer='delete.pt') 37 def delete(self): 38 print ('Deleted') 39 return {'page_title': 'Delete View'}
我们的主视图需要一个模板
more_view_classes/tutorial/home.pt
:<!DOCTYPE html> <html lang="en"> <head> <title>Quick Tutorial: ${view.view_name} - ${page_title}</title> </head> <body> <h1>${view.view_name} - ${page_title}</h1> <p>Go to the <a href="${request.route_url('hello', first='jane', last='doe')}">form</a>.</p> </body> </html>
上一节中的其他视图,同上
more_view_classes/tutorial/hello.pt
:<!DOCTYPE html> <html lang="en"> <head> <title>Quick Tutorial: ${view.view_name} - ${page_title}</title> </head> <body> <h1>${view.view_name} - ${page_title}</h1> <p>Welcome, ${view.full_name}</p> <form method="POST" action="${request.current_route_url()}"> <input name="new_name"/> <input type="submit" name="form.edit" value="Save"/> <input type="submit" name="form.delete" value="Delete"/> </form> </body> </html>
我们有一个编辑视图,在
more_view_classes/tutorial/edit.pt
:<!DOCTYPE html> <html lang="en"> <head> <title>Quick Tutorial: ${view.view_name} - ${page_title}</title> </head> <body> <h1>${view.view_name} - ${page_title}</h1> <p>You submitted <code>${new_name}</code></p> </body> </html>
最后,删除视图的模板位于
more_view_classes/tutorial/delete.pt
:<!DOCTYPE html> <html lang="en"> <head> <title>Quick Tutorial: ${page_title}</title> </head> <body> <h1>${view.view_name} - ${page_title}</h1> </body> </html>
我们的测试
more_view_classes/tutorial/tests.py
失败,我们来修改它们:1import unittest 2 3from pyramid import testing 4 5 6class TutorialViewTests(unittest.TestCase): 7 def setUp(self): 8 self.config = testing.setUp() 9 10 def tearDown(self): 11 testing.tearDown() 12 13 def test_home(self): 14 from .views import TutorialViews 15 16 request = testing.DummyRequest() 17 inst = TutorialViews(request) 18 response = inst.home() 19 self.assertEqual('Home View', response['page_title']) 20 21class TutorialFunctionalTests(unittest.TestCase): 22 def setUp(self): 23 from tutorial import main 24 app = main({}) 25 from webtest import TestApp 26 27 self.testapp = TestApp(app) 28 29 def test_home(self): 30 res = self.testapp.get('/', status=200) 31 self.assertIn(b'TutorialViews - Home View', res.body)
现在运行测试:
$VENV/bin/pytest tutorial/tests.py -q .. 2 passed in 0.40 seconds
运行 Pyramid 应用程序时使用:
$VENV/bin/pserve development.ini --reload
在浏览器中打开http://localhost:6543/howdy/jane/doe。单击
Save
和Delete
按钮,并在控制台窗口中查看输出。
分析¶
如您所见,这四个视图在逻辑上分组在一起。明确地:
我们有一个
home
可在http://localhost:6543/上通过点击链接查看hello
查看。当您转到时返回第二个视图
/howdy/jane/doe
. 此URL映射到hello
我们使用可选的@view_defaults
.当表单与
POST
方法。此规则在中指定@view_config
为了这个观点。单击按钮时返回第四个视图,如
<input type="submit" name="form.delete" value="Delete"/>
.
在此步骤中,我们将使用以下信息作为条件,说明如何确定要使用的视图:
HTTP请求的方法 (
GET
,POST
等)请求中的参数信息(提交的表单字段名)
我们还将视图配置的一部分集中到类级别, @view_defaults
,然后在一个视图中,仅为该视图重写该默认值。最后,我们通过共享将视图之间的共性置于视图类中:
状态分配位置
TutorialViews.__init__
计算值
然后在视图方法和模板(例如, ${{view.view_name}}
和 ${{view.full_name}}
)
注意,我们在模板中对如何生成URL进行了切换。我们以前对URL进行了硬编码,例如:
<a href="/howdy/jane/doe">Howdy</a>
在 home.pt
我们切换到:
<a href="${request.route_url('hello', first='jane',
last='doe')}">form</a>
Pyramid有丰富的功能来帮助以灵活、不易出错的方式生成URL。
额外credit¶
为什么我们的模板可以
${{view.full_name}}
也不必这么做${{view.full_name()}}
是吗?这个
edit
和delete
两个视图都接收POST
请求。为什么edit
视图配置未捕获POST
被使用delete
是吗?我们使用了Python。
@property
在full_name
. 如果我们在模板或视图代码中多次引用它,那么每次都会重新计算它。Pyramid是否提供将缓存属性的初始计算的内容?可以将多个路由与同一视图关联吗?
还有一个
request.route_path
应用程序编程接口。这和request.route_url
是吗?