3: Traversal Hierarchies¶
Objects with subobjects and views, all via URLs.
Background¶
In 2: Basic Traversal With Site Roots we took the simplest possible step: a root object with little need for the stitching together of a tree known as traversal.
In this step we remain simple, but make a basic hierarchy:
1/
2 doc1
3 doc2
4 folder1/
5 doc1
Objectives¶
Use a multi-level nested hierarchy of Python objects.
Show how
__name__
and__parent__
glue the hierarchy together.Use objects which last between requests.
Steps¶
We are going to use the previous step as our starting point:
$ cd ..; cp -r siteroot hierarchy; cd hierarchy $ $VENV/bin/python setup.py develop
Provide a richer set of objects in
hierarchy/tutorial/resources.py
:1class Folder(dict): 2 def __init__(self, name, parent, title): 3 self.__name__ = name 4 self.__parent__ = parent 5 self.title = title 6 7 8class Root(Folder): 9 pass 10 11 12class Document(object): 13 def __init__(self, name, parent, title): 14 self.__name__ = name 15 self.__parent__ = parent 16 self.title = title 17 18# Done outside bootstrap to persist from request to request 19root = Root('', None, 'My Site') 20 21 22def bootstrap(request): 23 if not root.values(): 24 # No values yet, let's make: 25 # / 26 # doc1 27 # doc2 28 # folder1/ 29 # doc1 30 doc1 = Document('doc1', root, 'Document 01') 31 root['doc1'] = doc1 32 doc2 = Document('doc2', root, 'Document 02') 33 root['doc2'] = doc2 34 folder1 = Folder('folder1', root, 'Folder 01') 35 root['folder1'] = folder1 36 37 # Only has to be unique in folder 38 doc11 = Document('doc1', folder1, 'Document 01') 39 folder1['doc1'] = doc11 40 41 return root
Have
hierarchy/tutorial/views.py
show information about the resource tree:1from pyramid.location import lineage 2from pyramid.view import view_config 3 4 5class TutorialViews: 6 def __init__(self, context, request): 7 self.context = context 8 self.request = request 9 self.parents = reversed(list(lineage(context))) 10 11 @view_config(renderer='templates/home.jinja2') 12 def home(self): 13 page_title = 'Quick Tutorial: Home' 14 return dict(page_title=page_title) 15 16 @view_config(name='hello', renderer='templates/hello.jinja2') 17 def hello(self): 18 page_title = 'Quick Tutorial: Hello' 19 return dict(page_title=page_title)
Update the
hierarchy/tutorial/templates/home.jinja2
view template:1{% extends "templates/layout.jinja2" %} 2{% block content %} 3 4 <ul> 5 <li><a href="/">Site Folder</a></li> 6 <li><a href="/doc1">Document 01</a></li> 7 <li><a href="/doc2">Document 02</a></li> 8 <li><a href="/folder1">Folder 01</a></li> 9 <li><a href="/folder1/doc1">Document 01 in Folder 01</a></li> 10 </ul> 11 12 <h2>{{ context.title }}</h2> 13 14 <p>Welcome to {{ context.title }}. Visit 15 <a href="{{ request.resource_url(context, 'hello') }}">hello</a> 16 </p> 17 18{% endblock content %}
The
hierarchy/tutorial/templates/breadcrumbs.jinja2
template now has a hierarchy to show:1{% for p in view.parents %} 2<span> 3 <a href="{{ request.resource_url(p) }}">{{ p.title }}</a> 4>> </span> 5{% endfor %}
Update the tests in
hierarchy/tutorial/tests.py
:1import unittest 2 3from pyramid.testing import DummyRequest 4from pyramid.testing import DummyResource 5 6 7class TutorialViewsUnitTests(unittest.TestCase): 8 def test_home_view(self): 9 from .views import TutorialViews 10 11 request = DummyRequest() 12 title = 'Dummy Context' 13 context = DummyResource(title=title, __name__='dummy') 14 inst = TutorialViews(context, request) 15 result = inst.home() 16 self.assertIn('Home', result['page_title']) 17 18 19class TutorialFunctionalTests(unittest.TestCase): 20 def setUp(self): 21 from tutorial import main 22 app = main({}) 23 from webtest import TestApp 24 self.testapp = TestApp(app) 25 26 def test_home(self): 27 result = self.testapp.get('/', status=200) 28 self.assertIn(b'Site Folder', result.body)
Now run the tests:
1$ $VENV/bin/nosetests tutorial 2.. 3---------------------------------------------------------------------- 4Ran 2 tests in 0.141s 5 6OK
Run your Pyramid application with:
$ $VENV/bin/pserve development.ini --reload
Open http://localhost:6543/ in your browser.
Analysis¶
In this example we have to manage our tree by assigning __name__
as an
identifier on each child, and __parent__
as a reference to the parent. The
template used now shows different information based on the object URL to which
you traversed.
We also show that @view_config
can set a "default" view on a context by
omitting the @name
attribute. Thus, if you visit
http://localhost:6543/folder1/
without providing anything after, the
configured default view is used.
Extra Credit¶
In
resources.py
, we moved the instantiation ofroot
out to global scope. Why?If you go to a resource that doesn't exist, will Pyramid handle it gracefully?
If you ask for a default view on a resource and none is configured, will Pyramid handle it gracefully?