3:遍历层次结构

Objects with subobjects and views, all via URLs.

背景

2:有立地根的基本导线测量

在这一步中,我们保持简单,但建立一个基本的层次结构:

1
2
3
4
5
/
   doc1
   doc2
   folder1/
      doc1

目标

  • 使用Python对象的多级嵌套层次结构。
  • 展示如何 __name____parent__
  • 使用在请求之间持续的对象。

步骤

  1. 我们将使用前面的步骤作为起点:

    $ cd ..; cp -r siteroot hierarchy; cd hierarchy
    $ $VENV/bin/python setup.py develop
    
  2. Provide a richer set of objects in hierarchy/tutorial/resources.py

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Folder(dict):
        def __init__(self, name, parent, title):
            self.__name__ = name
            self.__parent__ = parent
            self.title = title
    
    
    class Root(Folder):
        pass
    
    
    class Document(object):
        def __init__(self, name, parent, title):
            self.__name__ = name
            self.__parent__ = parent
            self.title = title
    
    # Done outside bootstrap to persist from request to request
    root = Root('', None, 'My Site')
    
    
    def bootstrap(request):
        if not root.values():
            # No values yet, let's make:
            # /
            #   doc1
            #   doc2
            #   folder1/
            #      doc1
            doc1 = Document('doc1', root, 'Document 01')
            root['doc1'] = doc1
            doc2 = Document('doc2', root, 'Document 02')
            root['doc2'] = doc2
            folder1 = Folder('folder1', root, 'Folder 01')
            root['folder1'] = folder1
    
            # Only has to be unique in folder
            doc11 = Document('doc1', folder1, 'Document 01')
            folder1['doc1'] = doc11
    
        return root
    
  3. hierarchy/tutorial/views.py

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from pyramid.location import lineage
    from pyramid.view import view_config
    
    
    class TutorialViews:
        def __init__(self, context, request):
            self.context = context
            self.request = request
            self.parents = reversed(list(lineage(context)))
    
        @view_config(renderer='templates/home.jinja2')
        def home(self):
            page_title = 'Quick Tutorial: Home'
            return dict(page_title=page_title)
    
        @view_config(name='hello', renderer='templates/hello.jinja2')
        def hello(self):
            page_title = 'Quick Tutorial: Hello'
            return dict(page_title=page_title)
    
  4. 更新 hierarchy/tutorial/templates/home.jinja2 查看模板:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {% extends "templates/layout.jinja2" %}
    {% block content %}
    
      <ul>
        <li><a href="/">Site Folder</a></li>
        <li><a href="/doc1">Document 01</a></li>
        <li><a href="/doc2">Document 02</a></li>
        <li><a href="/folder1">Folder 01</a></li>
        <li><a href="/folder1/doc1">Document 01 in Folder 01</a></li>
      </ul>
    
      <h2>{{ context.title }}</h2>
    
      <p>Welcome to {{ context.title }}. Visit
        <a href="{{ request.resource_url(context, 'hello') }}">hello</a>
      </p>
    
    {% endblock content %}
    
  5. 这个 hierarchy/tutorial/templates/breadcrumbs.jinja2

    1
    2
    3
    4
    5
    {% for p in view.parents %}
    <span>
      <a href="{{ request.resource_url(p) }}">{{ p.title }}</a>
    >> </span>
    {% endfor %}
    
  6. Update the tests in hierarchy/tutorial/tests.py

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import unittest
    
    from pyramid.testing import DummyRequest
    from pyramid.testing import DummyResource
    
    
    class TutorialViewsUnitTests(unittest.TestCase):
        def test_home_view(self):
            from .views import TutorialViews
    
            request = DummyRequest()
            title = 'Dummy Context'
            context = DummyResource(title=title, __name__='dummy')
            inst = TutorialViews(context, request)
            result = inst.home()
            self.assertIn('Home', result['page_title'])
    
    
    class TutorialFunctionalTests(unittest.TestCase):
        def setUp(self):
            from tutorial import main
            app = main({})
            from webtest import TestApp
            self.testapp = TestApp(app)
    
        def test_home(self):
            result = self.testapp.get('/', status=200)
            self.assertIn(b'Site Folder', result.body)
    
  7. 现在运行测试:

    $ $VENV/bin/nosetests tutorial
    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 0.141s
    
    OK
    
  8. 运行 Pyramid 应用程序时使用:

    $ $VENV/bin/pserve development.ini --reload
    
  9. 在浏览器中打开http://localhost:6543/。

分析

__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.

@view_config can set a "default" view on a context by omitting the @name 属性。Thus, if you visit http://localhost:6543/folder1/

Extra Credit

  1. resources.py root out to global scope. 为什么?
  2. 如果你去一个不存在的资源, Pyramid 会优雅地处理它吗?
  3. 如果您请求一个资源的默认视图,但未配置任何视图, Pyramid 会处理得当吗?