Step 04: Creating Basic Content

We are still in our simple content types (SiteFolder, Folder, and Document.) We haven’t yet switched back to Projector.

Before we do, though, let’s have the ability to add content. That is, folders allow adding subfolders or documents.

Goals

  • Arbitrarily nested content

Objectives

  • Simple form which POSTs data
  • A view which takes the POST data, creates a resource, and redirects to the newly-added resource

Steps

  1. $ cd ../../resources; mkdir step04; cd step04

  2. (Unchanged) Copy the following into step04/application.py:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from wsgiref.simple_server import make_server
    
    from pyramid.config import Configurator
    
    from resources import bootstrap
    
    
    def main():
        config = Configurator(root_factory=bootstrap)
        config.scan("views")
        app = config.make_wsgi_app()
        return app
    
    
    if __name__ == '__main__':
        app = main()
        server = make_server(host='0.0.0.0', port=8080, app=app)
        server.serve_forever()
    
  3. Copy the following into step04/views.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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    from random import randint
    
    from pyramid.decorator import reify
    from pyramid.httpexceptions import HTTPFound
    from pyramid.view import view_config
    
    from resources import SiteFolder
    from resources import Folder
    from resources import Document
    
    
    class ProjectorViews(object):
        def __init__(self, context, request):
            self.context = context
            self.request = request
    
        @view_config(renderer="templates/site_view.pt",
                     context=SiteFolder)
        def site_view(self):
            return {"children": self.context.values()}
    
        @reify
        def parent_info(self):
            parent = self.context.__parent__
            parent_url = self.request.resource_url(parent)
            return {
                'title': parent.title,
                'url': parent_url}
    
        @view_config(renderer="templates/folder_view.pt",
                     context=Folder)
        def folder_view(self):
            return {"children": self.context.values()}
    
        @view_config(name="add_folder", context=Folder)
        def add_folder_view(self):
            # Make a new Folder
            title = self.request.POST['folder_title']
            name = str(randint(0, 999999))
            new_folder = Folder(name, self.context, title)
            self.context[name] = new_folder
    
            # Redirect to the new folder
            url = self.request.resource_url(new_folder)
            return HTTPFound(location=url)
    
        @view_config(name="add_document", context=Folder)
        def add_document_view(self):
            # Make a new Document
            title = self.request.POST['document_title']
            name = str(randint(0, 999999))
            new_document = Document(name, self.context, title)
            self.context[name] = new_document
    
            # Redirect to the new document
            url = self.request.resource_url(new_document)
            return HTTPFound(location=url)
    
        @view_config(renderer="templates/document_view.pt",
                     context=Document)
        def document_view(self):
            return {}
    
  4. (Unchanged) Copy the following into step04/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
    class Folder(dict):
        def __init__(self, name, parent, title):
            self.__name__ = name
            self.__parent__ = parent
            self.title = title
    
    
    class SiteFolder(Folder):
        pass
    
    
    class Document(object):
        def __init__(self, name, parent, title):
            self.__name__ = name
            self.__parent__ = parent
            self.title = title
    
    root = SiteFolder('', None, 'Projector Site')
    
    def bootstrap(request):
        # 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
    
  5. Copy the following into step04/templates/document_view.pt:

    1
    2
    3
    4
    5
    6
    7
    <div>
        <h2>Document Title: ${context.title} in Folder
            <a
                    href="${view.parent_info.url}">${view.parent_info.title}</a>
            <a
        </h2>
    </div>
    
  6. Copy the following into step04/templates/folder_view.pt:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <div>
        <h2>Folder Title: ${context.title} in Folder
            <a
                    href="${view.parent_info.url}">${view.parent_info.title}</a>
        </h2>
        <ul>
            <li tal:repeat="child children">
                <a href="${view.request.resource_url(child)}">${child.title}</a>
            </li>
        </ul>
        <p>
            <form action="add_folder" method="POST">
                <label for="folder_title">Add: Folder
                    Title</label>
                <input name="folder_title"/>
                <input type="submit"/></form>
        </p>
    </div>
    
  7. Copy the following into step04/templates/site_view.pt:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <div>
        <h2>SiteFolder Title: ${context.title}</h2>
        <ul>
            <li tal:repeat="child children">
                <a href="${child.__name__}">${child.title}</a>
            </li>
        </ul>
        <p>
            <form action="add_folder" method="POST">
                <label for="folder_title">Add: Folder
                    Title</label>
                <input name="folder_title"/>
                <input type="submit"/></form>
            <form action="add_document" method="POST">
                <label for="document_title">Add: Document
                    Title</label>
                <input name="document_title"/>
                <input type="submit"/></form>
        </p>
    </div>
    
  8. Copy the following into step04/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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    import unittest
    
    from pyramid.testing import DummyRequest
    from pyramid.testing import DummyResource
    
    class DummySite(DummyResource):
        title = "Dummy Title"
        __name__ = "dummy"
        __parent__ = None
    
        def values(self):
            v = DummyResource(title="Dummy Item")
            return [v, v, v, v, v]
        
    class ProjectorViewsUnitTests(unittest.TestCase):
    
        def _makeOne(self, context, request):
            from views import ProjectorViews
            inst = ProjectorViews(context, request)
            return inst
    
        def test_site_view(self):
            request = DummyRequest()
            context = DummySite()
            inst = self._makeOne(context, request)
            result = inst.site_view()
            self.assertEqual(len(result['children']), 5)
    
        def test_folder_view(self):
            request = DummyRequest()
            context = DummySite()
            inst = self._makeOne(context, request)
            result = inst.folder_view()
            self.assertEqual(len(result['children']), 5)
    
        def test_document_view(self):
            request = DummyRequest()
            context = DummyResource()
            inst = self._makeOne(context, request)
            result = inst.document_view()
            self.assertEqual(result, {})
    
        def test_add_folder_view(self):
            from pyramid.httpexceptions import HTTPFound
            t = 'New Folder'
            request=DummyRequest(params={'folder_title': t})
            context = DummySite()
            inst = self._makeOne(context, request)
            result = inst.add_folder_view()
            # XXX Need a test that the item actually went in the folder
            self.assertEqual(HTTPFound, result.__class__)
    
        def test_add_document_view(self):
            from pyramid.httpexceptions import HTTPFound
            request=DummyRequest(folder_title='New Document')
            context = DummySite()
            inst = self._makeOne(context, request)
            result = inst.site_view()
            #self.assertEqual(HTTPFound, result.__class__)
            # XXX Need tests that the doc was actually added
    
    class ProjectorFunctionalTests(unittest.TestCase):
        def setUp(self):
            from application import main
            app = main()
            from webtest import TestApp
            self.testapp = TestApp(app)
    
        def test_it(self):
            res = self.testapp.get('/', status=200)
            self.assertTrue('SiteFolder' in res.body)
            res = self.testapp.get('/folder1', status=200)
            self.assertTrue('Folder' in res.body)
            res = self.testapp.get('/doc1', status=200)
            self.assertTrue('Document' in res.body)
            res = self.testapp.get('/doc2', status=200)
            self.assertTrue('Document' in res.body)
            res = self.testapp.get('/folder1/doc1', status=200)
            self.assertTrue('Document' in res.body)
    
  9. $ nosetests should report running 4 tests.

  10. $ python application.py

  11. Open http://127.0.0.1:8080/ in your browser.

Extra Credit

  1. Can document_view simply return nothing instead of an empty dictionary?
  2. Here’s a hard one...try eliminating the add views by turning folder_view into a self-posting form.

Analysis

To enforce uniqueness, we randomly choose a satisfactorily large number.

Discussion

  • Different strategies for handling uniqueness in a container
  • Concept of a content “factory” as expressed in past systems

Table Of Contents