4:类型特定视图¶
通过对类注册视图来键入特定的视图。
背景¶
在 3:遍历层次结构 我们有3种“内容类型”(根、文件夹和文档),但是,所有这些都使用相同的视图和模板。
Pyramid 遍历允许您将视图绑定到特定的内容类型。这种使URL“面向对象”的能力是遍历的一个显著特征,并使创建URL空间更加自然。Once Pyramid finds the context 对象在URL路径中,开发人员在视图谓词中具有很大的灵活性。
目标¶
使用装饰器
@view_config
其中使用context
将特定视图与之关联的属性context
特定类的实例。创建对特定类(也就是类型)唯一的视图和模板。
学习测试写作中的模式以处理多种上下文。
步骤¶
我们将使用前面的步骤作为起点:
$ cd ..; cp -r hierarchy typeviews; cd typeviews $ $VENV/bin/python setup.py develop
我们的观点
typeviews/tutorial/views.py
需要特定类型的注册:1from pyramid.location import lineage 2from pyramid.view import view_config 3 4from .resources import ( 5 Root, 6 Folder, 7 Document 8 ) 9 10 11class TutorialViews: 12 def __init__(self, context, request): 13 self.context = context 14 self.request = request 15 self.parents = reversed(list(lineage(context))) 16 17 @view_config(renderer='templates/root.jinja2', 18 context=Root) 19 def root(self): 20 page_title = 'Quick Tutorial: Root' 21 return dict(page_title=page_title) 22 23 @view_config(renderer='templates/folder.jinja2', 24 context=Folder) 25 def folder(self): 26 page_title = 'Quick Tutorial: Folder' 27 return dict(page_title=page_title) 28 29 30 @view_config(renderer='templates/document.jinja2', 31 context=Document) 32 def document(self): 33 page_title = 'Quick Tutorial: Document' 34 return dict(page_title=page_title)
我们的新内容子模板位于
typeviews/tutorial/templates/contents.jinja2
:1<h4>Contents</h4> 2<ul> 3 {% for child in context.values() %} 4 <li> 5 <a href="{{ request.resource_url(child) }}">{{ child.title }}</a> 6 </li> 7 {% endfor %} 8</ul>
创建用于查看根目录的模板
typeviews/tutorial/templates/root.jinja2
:1{% extends "templates/layout.jinja2" %} 2{% block content %} 3 4 <h2>{{ context.title }}</h2> 5 <p>The root might have some other text.</p> 6 {% include "templates/contents.jinja2" %} 7 8{% endblock content %}
现在制作一个用于查看文件夹的模板
typeviews/tutorial/templates/folder.jinja2
:1{% extends "templates/layout.jinja2" %} 2{% block content %} 3 4 <h2>{{ context.title }}</h2> 5 {% include "templates/contents.jinja2" %} 6 7{% endblock content %}
最后制作一个用于查看文档的模板
typeviews/tutorial/templates/document.jinja2
:1{% extends "templates/layout.jinja2" %} 2{% block content %} 3 4 <h2>{{ context.title }}</h2> 5 <p>A document might have some body text.</p> 6 7{% endblock content %}
需要更多的测试
typeviews/tutorial/tests.py
:1import unittest 2 3from pyramid.testing import DummyRequest 4from pyramid.testing import DummyResource 5 6 7class TutorialViewsUnitTests(unittest.TestCase): 8 def _makeOne(self, context, request): 9 from .views import TutorialViews 10 11 inst = TutorialViews(context, request) 12 return inst 13 14 def test_site(self): 15 request = DummyRequest() 16 context = DummyResource() 17 inst = self._makeOne(context, request) 18 result = inst.root() 19 self.assertIn('Root', result['page_title']) 20 21 def test_folder_view(self): 22 request = DummyRequest() 23 context = DummyResource() 24 inst = self._makeOne(context, request) 25 result = inst.folder() 26 self.assertIn('Folder', result['page_title']) 27 28 def test_document_view(self): 29 request = DummyRequest() 30 context = DummyResource() 31 inst = self._makeOne(context, request) 32 result = inst.document() 33 self.assertIn('Document', result['page_title']) 34 35 36class TutorialFunctionalTests(unittest.TestCase): 37 def setUp(self): 38 from tutorial import main 39 app = main({}) 40 from webtest import TestApp 41 self.testapp = TestApp(app) 42 43 def test_it(self): 44 res = self.testapp.get('/', status=200) 45 self.assertIn(b'Root', res.body) 46 res = self.testapp.get('/folder1', status=200) 47 self.assertIn(b'Folder', res.body) 48 res = self.testapp.get('/doc1', status=200) 49 self.assertIn(b'Document', res.body) 50 res = self.testapp.get('/doc2', status=200) 51 self.assertIn(b'Document', res.body) 52 res = self.testapp.get('/folder1/doc1', status=200) 53 self.assertIn(b'Document', res.body)
$ $VENV/bin/nosetests
应报告正在运行4个测试。运行 Pyramid 应用程序时使用:
$ $VENV/bin/pserve development.ini --reload
在浏览器中打开http://localhost:6543/。
分析¶
对于最重要的变化,我们的 @view_config
现在比赛在 context
视图谓词。我们可以说“在观看时使用此视图” this 类似的事情,“路由作为URL和视图之间的中间步骤的概念已经被消除。
Extra Credit¶
您应该计算Python端的子列表,还是通过在上下文上操作在模板端访问它?
如果需要不同的遍历策略呢?
在Zope, interfaces 用于注册视图。如何针对支持特定接口的实例注册 Pyramid 视图?你应该什么时候?
假设您需要在类的特定实例上使用一个更具体的视图,让一个更一般的视图覆盖所有其他实例。你有哪些选择?