5: Adding Resources To Hierarchies¶
Multiple views per type allowing addition of content anywhere in a resource tree.
Background¶
We now have multiple kinds of things, but only one view per resource type. We need the ability to add things to containers, then view and edit resources.
We will use the previously mentioned concept of named views. A name is a part of the URL that appears after the resource identifier. For example:
@view_config(context=Folder, name='add_document')
...means that this URL:
http://localhost:6543/some_folder/add_document
...will match the view being configured. It's as if you have an object-oriented web with operations on resources represented by a URL.
Goals¶
Allow adding and editing content in a resource tree.
Create a simple form which POSTs data.
Create a view which takes the POST data, creates a resource, and redirects to the newly-added resource.
Create per-type named views.
Steps¶
We are going to use the previous step as our starting point:
$ cd ..; cp -r typeviews addcontent; cd addcontent $ $VENV/bin/python setup.py develop
Our views in
addcontent/tutorial/views.py
need type-specific registrations:1from random import randint 2 3from pyramid.httpexceptions import HTTPFound 4from pyramid.location import lineage 5from pyramid.view import view_config 6 7from .resources import ( 8 Root, 9 Folder, 10 Document 11 ) 12 13 14class TutorialViews(object): 15 def __init__(self, context, request): 16 self.context = context 17 self.request = request 18 self.parents = reversed(list(lineage(context))) 19 20 @view_config(renderer='templates/root.jinja2', 21 context=Root) 22 def root(self): 23 page_title = 'Quick Tutorial: Root' 24 return dict(page_title=page_title) 25 26 @view_config(renderer='templates/folder.jinja2', 27 context=Folder) 28 def folder(self): 29 page_title = 'Quick Tutorial: Folder' 30 return dict(page_title=page_title) 31 32 @view_config(name='add_folder', context=Folder) 33 def add_folder(self): 34 # Make a new Folder 35 title = self.request.POST['folder_title'] 36 name = str(randint(0, 999999)) 37 new_folder = Folder(name, self.context, title) 38 self.context[name] = new_folder 39 40 # Redirect to the new folder 41 url = self.request.resource_url(new_folder) 42 return HTTPFound(location=url) 43 44 @view_config(name='add_document', context=Folder) 45 def add_document(self): 46 # Make a new Document 47 title = self.request.POST['document_title'] 48 name = str(randint(0, 999999)) 49 new_document = Document(name, self.context, title) 50 self.context[name] = new_document 51 52 # Redirect to the new document 53 url = self.request.resource_url(new_document) 54 return HTTPFound(location=url) 55 56 @view_config(renderer='templates/document.jinja2', 57 context=Document) 58 def document(self): 59 page_title = 'Quick Tutorial: Document' 60 return dict(page_title=page_title)
Make a re-usable snippet in
addcontent/tutorial/templates/addform.jinja2
for adding content:1<p> 2 <form class="form-inline" 3 action="{{ request.resource_url(context, 'add_folder') }}" 4 method="POST"> 5 <div class="form-group"> 6 <input class="form-control" name="folder_title" 7 placeholder="New folder title..."/> 8 </div> 9 <input type="submit" class="btn" value="Add Folder"/> 10 </form> 11</p> 12<p> 13 <form class="form-inline" 14 action="{{ request.resource_url(context, 'add_document') }}" 15 method="POST"> 16 <div class="form-group"> 17 <input class="form-control" name="document_title" 18 placeholder="New document title..."/> 19 </div> 20 <input type="submit" class="btn" value="Add Document"/> 21 </form> 22</p>
Add this snippet to
addcontent/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 {% include "templates/addform.jinja2" %} 9 10{% endblock content %}
Forms are needed in
addcontent/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 {% include "templates/addform.jinja2" %} 8 9{% endblock content %}
$ $VENV/bin/nosetests
should report running 4 tests.Run your Pyramid application with:
$ $VENV/bin/pserve development.ini --reload
Open http://localhost:6543/ in your browser.
Analysis¶
Our views now represent a richer system, where form data can be processed to modify content in the tree. We do this by attaching named views to resource types, giving them a natural system for object-oriented operations.
To mimic uniqueness, we randomly choose a satisfactorily large number. For true uniqueness, we would also need to check that the number does not already exist at the same level of the resource tree.
We'll start to address a couple of issues brought up in the Extra Credit below in the next step of this tutorial, 6: Storing Resources In ZODB.
Extra Credit¶
What happens if you add folders and documents, then restart your app?
What happens if you remove the pseudo-random, pseudo-unique naming convention and replace it with a fixed value?