foundev.github.io

"Python Web Framework Series – Pylons: Part 3 Views with Mako"

This is a huge post and I should have split this into several smaller ones so please bear with me while I get my series format tweaked. We last left off with Controllers, Views and Testing with a basic test, basic view and basic controller.  Now with our basic scaffold built we can focus on making our views reusable, dynamic, and more pleasant to look at.

First things first

First before we dive too deeply into Mako, lets setup a default home page by doing the following:

  1. delete the index.html in the pylonsforumpublic folder.
  2. in pylonsforumconfigrouting.py add the following line somewhere under the line “# CUSTOM ROUTES HERE” but before “return map”.
    1. map.connect(“home”, “/”, controller=”home”, action=”index”)
  3. assuming you have your Pylons command we used earlier  paster serve –-reload development.ini then open your browser to http://127.0.0.1:5000 and you should see the view we created in the last lesson.

###

Basics

 

Mako uses standard python mixed with HTML to display views. This gives you both a great deal of flexibility and a great deal of ability to hang yourself. So be warned, avoid putting to much logic into your view, since you can do about anything there.

Open up templatesindex.mako __replace the table rows in the table body with the following:

</b></span>
%for p in c.posts:
</b></span>${p.author}</td></b></span>${p.subject}</td></b></span>${p.dateadded}</td></tr>
%endfor 
</tbody> </div> </div> for good measure in the beginning of the body before the “recent posts” div place the following
</b></span>
username: ${c.username}                
<div id=“recentposts” style=“float: right”> </div> </div> Looking at this, for the tables we’ve done a for loop over some variable called c.posts. In the second we’re accessing another variable c.username. “c” is shorthand for Template Context, similar to ViewData in Asp.Net MVC and PropertyBag in Monorail, except we’re not accessing a string dictionary.  Add the following tests in “**pylonsforumtestsfunctionaltest_home.py**”  for what we need to add to the controller:
    def test_username(self):
        response = self.app.get(url(controller=‘home’, action=‘index’))
        assert “username: rsvihla” in response </p>

    def test_recent_posts(self):
        response = self.app.get(url(controller=‘home’, action=‘index’))
        assert “jkruseNew Kindle06/24/2009” in response </div> </div>

running nosetests –-with-pylons=test.init should give you two assertion failures.

now change “pylonsforumcontrollershome.py” to look like so:

import logging </p>

from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to

from pylonsforum.lib.base import BaseController, render

log = logging.getLogger(__name__)

class Post(object):

    def __init__(self, author, subject, dateadded):
        self.author = author
        self.subject = subject
        self.dateadded = dateadded

class HomeController(BaseController):

    def index(self):
        c.username = “rsvihla”
        c.posts = [Post(“jkruse”, “New Kindle”, “06/24/2009”)]
        return render(‘index.mako’) </div> </div>

We’ve added a Post class with basic attributes and placed them in an array in the c.posts variable, also we’ve hardcoded the username “rsvihla”.  I know the more experience developers here are cringing at my awful little Post class, don’t worry its a just a place holder and will be removed later.  The point here is building functionality in steps with test coverage.  Now run nosetests just as before and you should have all tests passing.  For bonus measure refresh http://127.0.0.1:5000 .

Base Layouts

We’ve built a very basic page now, but suppose we want to build several and have some bits of information show up over and over again, like user name or menu structure.

create a base template named “base.mako” in the templates directory that has the following in it:

<%namespace name=“user” module=“pylonsforum.model.users” />
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
</b></span>
</b></span>
</b></span><span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>title()<span style="color: #bc7a00">}</span><span style="color: #008000"><b></title></b></span><br /> <span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>head_tags()<span style="color: #bc7a00">}</span><br /> <span style="color: #008000"><b><style </b></span><span style="color: #7d9029">type=</span><span style="color: #ba2121">“text/css”</span><span style="color: #008000"><b>></b></span><br /> <span style="color: #bc7a00">#header{ </span><br /> <span style="color: #008000"><b>height</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>70px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>width</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>100</b></span><span style="color: #666666">%;</span><br /> <span style="color: #008000"><b>border-width</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b></b></span><span style="color: #0000ff"><b>.5px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>border-style</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>solid</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>font-size</b></span><span style="color: #aa22ff">:30px</span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>text-align</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>center</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>background-color</b></span><span style="color: #666666">:</span> <span style="color: #0000ff">#9B2C25</span><span style="color: #666666">;</span><br /> }<br /> <span style="color: #bc7a00">#sidebar{</span><br /> <span style="color: #008000"><b>background-color</b></span><span style="color: #666666">:</span> <span style="color: #0000ff">#C1AD72</span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>border-width</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b></b></span><span style="color: #0000ff"><b>.5px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>border-style</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>dotted</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>width</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>150px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>height</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>600px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>float</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>right</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>font-size</b></span><span style="color: #666666">:</span> <span style="color: #008000"><b>15px</b></span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>padding-top</b></span><span style="color: #aa22ff">:10px</span><span style="color: #666666">;</span><br /> <span style="color: #008000"><b>padding-left</b></span><span style="color: #aa22ff">:5px</span><span style="color: #666666">;</span><br /> }<br /> <span style="color: #bc7a00">#content{</span><br /> <span style="color: #008000"><b>margin-top</b></span><span style="color: #aa22ff">:10px</span><span style="color: #666666">;</span><br /> }<br /> <span style="color: #008000"><b>body</b></span> {<br /> <span style="color: #008000"><b>background-color</b></span><span style="color: #666666">:</span> <span style="color: #666666">#DACEAB</span>;<br /> }<br /> <span style="color: #008000"><b></style></b></span><br /> <span style="color: #008000"><b></head></b></span><br /> <span style="color: #008000"><b><body></b></span></p> <p> <span style="color: #008000"><b><div</b></span> <span style="color: #7d9029">id=</span><span style="color: #ba2121">“header”</span><span style="color: #008000"><b>></b></span>Pylons Forum<span style="color: #008000"><b></div></b></span><br /> <span style="color: #008000"><b><div</b></span> <span style="color: #7d9029">id=</span><span style="color: #ba2121">“sidebar”</span><span style="color: #008000"><b>></b></span><br /> username: <span style="color: #bc7a00">${</span>user<span style="color: #666666">.</span>get_current_user()<span style="color: #bc7a00">}</span><br /> <span style="color: #008000"><b></div></b></span> </p> <p> <span style="color: #008000"><b><div</b></span> <span style="color: #7d9029">id=</span><span style="color: #ba2121">“content”</span> <span style="color: #008000"><b>></b></span><br /> <span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>body()<span style="color: #bc7a00">}</span><br /> <span style="color: #008000"><b></div></b></span> </p> <p> <span style="color: #008000"><b></div></b></span><br /> <span style="color: #008000"><b></body></b></span><br /> <span style="color: #008000"><b></html></b></span> </div> </div> <p> Most everything is standard HTML so lets restrict this to the interesting bits: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #bc7a00"><%</span><span style="color: #008000">namespace</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“user”</span> <span style="color: #7d9029">module=</span><span style="color: #ba2121">“pylonsforum.model.users”</span> <span style="color: #bc7a00">/></span> </div> </div> <p> The namespace directive here is basically doing an import of a custom module and giving it an alias of user. This is no different than in normal python code typing: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b>import</b></span> <span style="color: #0000ff"><b>pylonsforum.model.users</b></span> <span style="color: #008000"><b>as</b></span> <span style="color: #0000ff"><b>user</b></span> </div> </div> <p>   </p> <p> This is used later in the div sidebar by pulling the current user from my customer users module: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> username : <span style="color: #bc7a00">${</span>user<span style="color: #666666">.</span>get_current_user()<span style="color: #bc7a00">}</span> </div> </div> <p> the module source only contains a simple hard coded value for now so in <b>pylonsforummodel </b>make a <b>users.py</b> file with only the following: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">get_current_user</span>(<span style="color: #008000">self</span>):<br />     <span style="color: #008000"><b>return</b></span> <span style="color: #ba2121">“rsvihla”</span> </div> </div> <p>   </p> <hr /> <p>   </p> <p> Now onto the next non-HTML tidbits </p> <p>   </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b><title></b></span><span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>title()<span style="color: #bc7a00">}</span><span style="color: #008000"><b></title></b></span><br /> <span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>head_tags()<span style="color: #bc7a00">}</span><br /> <span style="color: #bc7a00">${</span><span style="color: #008000">self</span><span style="color: #666666">.</span>body()<span style="color: #bc7a00">}</span> </div> </div> <p>   </p> <p> Everyone of the above methods establishes a base method that the child templates must now call with the exception of self.body (which is called when the templates actually render the content anyway).  So lets adjust “index.mako” template to the following: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #bc7a00"><%</span><span style="color: #008000">inherit</span> <span style="color: #7d9029">file=</span><span style="color: #ba2121">“/base.mako”</span><span style="color: #bc7a00">/></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“title()”</span><span style="color: #bc7a00">></span>Pylons Forum<span style="color: #bc7a00"></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“head_tags()”</span><span style="color: #bc7a00">></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #008000"><b><div</b></span> <span style="color: #7d9029">id=</span><span style="color: #ba2121">“recentposts”</span><span style="color: #008000"><b>></b></span><br /> <span style="color: #008000"><b><table></b></span><br /> <span style="color: #008000"><b><thead><tr><th></b></span>subject<span style="color: #008000"><b></th><th></b></span>author<span style="color: #008000"><b></th><th></b></span>date submitted<span style="color: #008000"><b></th></tr></thead></b></span><br /> <span style="color: #008000"><b><tbody></b></span><br /> <span style="color: #bc7a00">%</span><span style="color: #008000"><b>for</b></span> p <span style="color: #aa22ff"><b>in</b></span> c<span style="color: #666666">.</span>posts:<br />     <span style="color: #008000"><b><tr><td></b></span><span style="color: #bc7a00">${</span>p<span style="color: #666666">.</span>author<span style="color: #bc7a00">}</span><span style="color: #008000"><b></td><td></b></span><span style="color: #bc7a00">${</span>p<span style="color: #666666">.</span>subject<span style="color: #bc7a00">}</span><span style="color: #008000"><b></td><td></b></span><span style="color: #bc7a00">${</span>p<span style="color: #666666">.</span>dateadded<span style="color: #bc7a00">}</span><span style="color: #008000"><b></td></tr></b></span><br /> <span style="color: #bc7a00">%</span>endfor <br /> <span style="color: #008000"><b></tbody></b></span><br /> <span style="color: #008000"><b></table></b></span><br /> <span style="color: #008000"><b></div></b></span> </div> </div> <p> The inherit line at the very top references our base.mako template. The next two lines with the <%def tag are where our previous self.head_tags() and self.title() methods are referenced. </p> <p> You’ll notice the title method is passing in a new title for the page, and our head_tags method is doing nothing, so no changes there. Browse to your pylons home page and you should see something resembling this: </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_024C9A53.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_4B061317.png" width="624" border="0" height="412" /></a> </p> <p> Your tests should also still pass with no modification. </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_296A8FE0.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_4436B5EC.png" width="655" border="0" height="91" /></a> </p> <h3> Forms and Web Helpers </h3> <p> Lets add a basic submit form and a link in the base.mako template to go to the new form page. </p> <p> Add the following tests to <b>test_home.py</b> </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace">     <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">test_new_thread</span>(<span style="color: #008000">self</span>):<br />         response <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>app<span style="color: #666666">.</span>get(url(controller<span style="color: #666666">=</span><span style="color: #ba2121">“home”</span>, action<span style="color: #666666">=</span><span style="color: #ba2121">“newthread”</span>))<br />         <span style="color: #008000"><b>assert</b></span> <span style="color: #ba2121">“<input id=</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121">subject</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121"> name=</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121">subject</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121"> type=</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121">text</span><span style="color: #bb6622"><b>“</b></span><span style="color: #ba2121"> />”</span> <span style="color: #aa22ff"><b>in</b></span> response<br />     <br />     <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">test_submit_new_thread</span>(<span style="color: #008000">self</span>):<br />         response <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>app<span style="color: #666666">.</span>post(url(controller<span style="color: #666666">=</span><span style="color: #ba2121">“home”</span>, action<span style="color: #666666">=</span><span style="color: #ba2121">“submitnewthread”</span>), params<span style="color: #666666">=</span>{<span style="color: #ba2121">‘subject’</span>:<span style="color: #ba2121">‘test’</span>,<span style="color: #ba2121">‘content’</span>:<span style="color: #ba2121">‘testcontent’</span>})<br />         <span style="color: #008000"><b>assert</b></span> <span style="color: #ba2121">“post submitted”</span> <span style="color: #aa22ff"><b>in</b></span> response </div> </div> <p> Install FormBuild with the following command: </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_786ADF32.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_61AB97A9.png" width="647" border="0" height="39" /></a> </p> <p> Next open <b>libhelpers.py </b>and change the imports to as follows: </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_080D7AF5.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_181C49AC.png" width="655" border="0" height="85" /></a> </p> <p> In the sidebar div under username add </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b><br /></b></span><br /> <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>link_to(<span style="color: #ba2121">‘new thread’</span>, h<span style="color: #666666">.</span>url_for(controller<span style="color: #666666">=</span><span style="color: #ba2121">‘home’</span>, action<span style="color: #666666">=</span><span style="color: #ba2121">‘newthread’</span>))<span style="color: #bc7a00">}</span> </div> </div> <p> Now add an action on the home controller for :”new thread” and create a view in your templates folder called <b>newthread.mako</b>. </p> <p> Add the following to your <b>home.py</b> controller: </p> <p>   </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace">     <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">newthread</span>(<span style="color: #008000">self</span>):<br />         <span style="color: #008000"><b>return</b></span> render(<span style="color: #ba2121">‘newthread.mako’</span>) </div> </div> <h3> </h3> <h3>   </h3> <p> The newthread.mako file should have the following in it: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #bc7a00"><%</span><span style="color: #008000">inherit</span> <span style="color: #7d9029">file=</span><span style="color: #ba2121">“/base.mako”</span><span style="color: #bc7a00">/></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“title()”</span><span style="color: #bc7a00">></span>Make A New Thread<span style="color: #bc7a00"></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“head_tags()”</span><span style="color: #bc7a00">></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>form_start(h<span style="color: #666666">.</span>url_for(controller<span style="color: #666666">=</span><span style="color: #ba2121">‘home’</span>, action<span style="color: #666666">=</span><span style="color: #ba2121">‘submitnewthread’</span>), method<span style="color: #666666">=</span><span style="color: #ba2121">“post”</span>)<span style="color: #bc7a00">}</span><br />     <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>field(<span style="color: #ba2121">“Subject”</span>, field<span style="color: #666666">=</span>h<span style="color: #666666">.</span>text(name<span style="color: #666666">=</span><span style="color: #ba2121">‘subject’</span>))<span style="color: #bc7a00">}</span><br />     <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>field(<span style="color: #ba2121">“Content”</span> ,field<span style="color: #666666">=</span>h<span style="color: #666666">.</span>textarea(name<span style="color: #666666">=</span><span style="color: #ba2121">‘content’</span>))<span style="color: #bc7a00">}</span> <br />     <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>field(field<span style="color: #666666">=</span>h<span style="color: #666666">.</span>submit(value<span style="color: #666666">=</span><span style="color: #ba2121">“Create Thread”</span>, name<span style="color: #666666">=</span><span style="color: #ba2121">‘submit’</span>))<span style="color: #bc7a00">}</span><br /> <span style="color: #bc7a00">${</span>h<span style="color: #666666">.</span>form_end()<span style="color: #bc7a00">}</span> </div> </div> <p> Sooooooo what are h values everywhere?  Do I need them? They come from our earlier imports in <b><span style="text-decoration: underline">l</span>ibhelpers.py , </b>and no you do not actually need to use them, a typical HTML form tag is all you actually need, this is an available option for those interested. </p> <ul> <li> h.form_start and h.form_end are pretty self explanatory </li> <li> h.field takes label text as argument one, and then a url for argument two. </li> <li> h.text, h.textarea, h.hidden and h.submit represent their html counterparts. </li> <li> h.url_for takes a controller and action to make a url for you. </li> </ul> <p>   </p> <hr /> <p> We now need another action for actually posting our data. so add another method to our <b>home.py</b> controller: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace">      <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">submitnewthread</span>(<span style="color: #008000">self</span>):<br />         c<span style="color: #666666">.</span>username <span style="color: #666666">=</span> users<span style="color: #666666">.</span>get_current_user(<span style="color: #008000">self</span>)<br />         c<span style="color: #666666">.</span>subject <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #ba2121">‘subject’</span>]<br />         c<span style="color: #666666">.</span>content <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #ba2121">‘content’</span>]<br />         <span style="color: #008000"><b>return</b></span> render(<span style="color: #ba2121">‘submitnewthread.mako’</span>) </div> </div> <p> With the following import at the top </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b>import</b></span> <span style="color: #0000ff"><b>pylonsforum.model.users</b></span> </div> </div> <p>   </p> <p> Our <b>submitnewthread.mako</b> view should have the following: </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #bc7a00"><%</span><span style="color: #008000">inherit</span> <span style="color: #7d9029">file=</span><span style="color: #ba2121">“/base.mako”</span><span style="color: #bc7a00">/></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“title()”</span><span style="color: #bc7a00">></span>Thank You For Your Submission<span style="color: #bc7a00"></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #bc7a00"><%</span><span style="color: #008000">def</span> <span style="color: #7d9029">name=</span><span style="color: #ba2121">“head_tags()”</span><span style="color: #bc7a00">></%</span><span style="color: #008000">def</span><span style="color: #bc7a00">></span><br /> <span style="color: #008000"><b><p></b></span>post submitted <span style="color: #008000"><b></p></b></span><br /> data was as follows <span style="color: #008000"><b><br /></b></span><br /> author:  <span style="color: #bc7a00">${</span>c<span style="color: #666666">.</span>username<span style="color: #bc7a00">}</span> <span style="color: #008000"><b><br /></b></span><br /> subject: <span style="color: #bc7a00">${</span>c<span style="color: #666666">.</span>subject<span style="color: #bc7a00">}</span> <span style="color: #008000"><b><br</b></span> <span style="color: #008000"><b>/></b></span><br /> content: <span style="color: #bc7a00">${</span>c<span style="color: #666666">.</span>content<span style="color: #bc7a00">}</span> <span style="color: #008000"><b><br</b></span> <span style="color: #008000"><b>/></b></span> </div> </div> <p> newthread view should look like so </p> <h3> <a href="//lostechies.com/ryansvihla/files/2011/03/image_0C868C6D.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_67F4FEE8.png" width="595" border="0" height="376" /></a> </h3> <p> and submitnewthread page should look like this </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_63124B2C.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_10936AF0.png" width="598" border="0" height="524" /></a> </p> <h3> Validation </h3> <p>   </p> <p> Validation is a huge subject in Pylons, I’m going to just focus on the most basic common case. </p> <p> Open <b>home.py </b>controller again and add these two imports to the top </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b>import</b></span> <span style="color: #0000ff"><b>formencode</b></span><br /> <span style="color: #008000"><b>from</b></span> <span style="color: #0000ff"><b>pylons.decorators</b></span> <span style="color: #008000"><b>import</b></span> validate </div> </div> <p>   </p> <p> Then in the same file for now below the post object add </p> <p>   </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace"> <span style="color: #008000"><b>class</b></span> <span style="color: #0000ff"><b>PostForm</b></span>(formencode<span style="color: #666666">.</span>Schema):<br />     allow_extra_fields <span style="color: #666666">=</span> <span style="color: #008000">True</span><br />     filter_extra_fields <span style="color: #666666">=</span> <span style="color: #008000">True</span><br />     subject <span style="color: #666666">=</span> formencode<span style="color: #666666">.</span>validators<span style="color: #666666">.</span>String(not_empty<span style="color: #666666">=</span><span style="color: #008000">True</span>)<br />     content <span style="color: #666666">=</span> formencode<span style="color: #666666">.</span>validators<span style="color: #666666">.</span>String(not_empty<span style="color: #666666">=</span><span style="color: #008000">True</span>) </div> </div> <p>   </p> <p> Finally on top of the submitnewthead action in the same class file add </p> <div style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px" class="wlWriterEditableSmartContent"> <div style="font-family:consolas,lucida console,courier,monospace">     <span style="color: #aa22ff">@validate</span>(schema<span style="color: #666666">=</span>PostForm(), form<span style="color: #666666">=</span><span style="color: #ba2121">‘newthread’</span>, post_only<span style="color: #666666">=</span><span style="color: #008000">True</span>, on_get<span style="color: #666666">=</span><span style="color: #008000">True</span>)<br />     <span style="color: #008000"><b>def</b></span> <span style="color: #0000ff">submitnewthread</span>(<span style="color: #008000">self</span>): </div> </div> <p> Now if you attempt to leave either the subject or content fields blank PylonsForum will now notify you of what you missed. </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_26E91035.png"><img style="border-right-width: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_546A2FF8.png" width="605" border="0" height="388" /></a> </p> <p>   </p> <p> When done your tests should all pass </p> <p> <a href="//lostechies.com/ryansvihla/files/2011/03/image_17F14468.png"><img style="border-bottom: 0px;border-left: 0px;border-top: 0px;border-right: 0px" alt="image" src="//lostechies.com/ryansvihla/files/2011/03/image_thumb_5E6E3470.png" width="602" border="0" height="91" /></a> </p> <h3> Summary </h3> <p> We went through the 80% cases for views, controllers and a brief bit about testing the response object. We still haven’t done a lot of unit testing in the traditional sense but we’ve been focusing exclusively on UI.  Stay tuned next for my SQL Alchemy piece. </p> </p></b></span></div></div></td></b></span></td></b></span></td></tr></b></span></tbody></b></span></th></b></span></th></b></span></th></tr></thead></b></span></table></b></span></div></div>