2. TurboGears2
● Framework for rapid development encouraging
customization
● Object Dispatch based, regular expressions can get messy,
you will never have to write one anymore
● By default an XML template engine with error detection
● Declarative Models with transactional unit of work
● Built in Validation, Authentication, Authorization, Caching,
Sessions, Migrations, MongoDB Support and many more.
3. OverView
CONTROLLER TEMPLATE
class RootController(BaseController): <html xmlns="http://www.w3.org/1999/xhtml"
error = ErrorController() xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
@expose('wiki20.templates.index')
def index(self): <xi:include href="master.html" />
return dict(page='index')
<head>
<title>My Title</title>
</head>
MODEL
<body>
class Page(DeclarativeBase): <h1>${page}</h1>
__tablename__ = 'pages' <div py:for="num in range(10)">${num}</div>
</body>
id = Column(Integer, primary_key=True) </html>
pagename = Column(Text, unique=True)
data = Column(Text)
5. Declarative Model
current_page = DBSession.query(Page).filter_by(pagename='index').first()
pages = DBSession.query(Page).all()
subpages = DBSession.query(Page).filter(Page.pagename.like('index_%')).all()
TurboGears does this for us if DBSession.add(Page(pagename='index_about',
no errors appeared during the data='About Content'))
request. The Unit of Work will DBSession.flush()
reorder insertions as required transaction.commit()
by dependencies.
TurboGears will automatically commit transactions on any session used inside the request if no error
appears, otherwise a rollback is issued to avoid messing up the database.
7. Quickstarting an Application
$ virtualenv --no-site-packages -p python2.6 tg2env
$ cd tg2env/
$ source bin/activate
(tg2env)$ easy_install -i http://tg.gy/current tg.devtools
(tg2env)$ paster quickstart example
(tg2env)$ cd example/
(tg2env)$ python setup.py develop
(tg2env)$ paster setup-app development.ini
(tg2env)$ paster serve development.ini
● We use a virtual environment not to mess with our system wide packages
● Using TurboGears private index will permit to install the exact right version of the packages
● The quickstart command will create for us a new project with authentication, authorization and
models.
● We use the setup.py develop command to install the project to be able to run it
● The setup-app command will initialize the database with a bunch of users for us, we can avoid this
if we choose not to enable authentication
● Last we can call the serve command to run out application
10. Photo Galleries
Install tgapp-photos pluggable
(tg2env)$ easy_install tgapp-photos applications for photo albums support
(tg2env)$ mkdir example/public/attachments
from tgext.pluggable import plug We Plug tgapp-photos to have ready to
plug(base_config, 'photos') use photo albums support
<body>
<div id="getting_started">
${h.call_partial('photos.partials:albums')} Our Template ends being just a call to
the partial that renders the previews of
<div style="clear:both"/> our albums
</div>
</body>
.photos_gallery {
display: block;
float: left; Some CSS to give a minimal look to our
gallery
text-align: center;
margin-left: 10px;
margin-bottom: 10px;
}
12. I want my blog, Now !
Install tgapp-smallpress for blogs
(tg2env)$ easy_install tgapp-smallpress support and create a directory
where it can store attachments
from tgext.pluggable import plug We Plug tgapp-photos to have
plug(base_config, 'photos') ready to use photo albums
support
<body>
<div id="getting_started">
${h.call_partial('photos.partials:albums')} Add call to smallpress partial to
<div style="clear:both"/> render the list of articles of the
${h.call_partial('smallpress.partials:articles')} default blog
</div>
</body>
#smallpress_articles { margin-top: 30px; }
.smallpress_article h2 { margin-bottom: 0; } Our usual bunch of minimal styling.
.smallpress_article h2 a { text-decoration: none; }
.smallpress_article h4 { margin-top: 0; }
14. What about the other pages?
What's up with those About, Authentication,
WSGI and Contact pages? We don't need them
for our personal website!
Let's remove all the methods in
RootController between index and login
and their templates!
15. Doh!
Well, actually we need to remove also the links
inside the menu bar. Those are defined into
master.html which is the master template
that every page inherits.
16. Let's now add wiki to our site
First of all our model, it's just a page with a title
and a content.
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, Integer, DateTime, Text
from sqlalchemy.orm import relation, synonym
from example.model import DeclarativeBase
class WikiPage(DeclarativeBase):
__tablename__ = 'wikipages'
uid = Column(Integer, primary_key=True)
pagename = Column(Text, unique=True)
data = Column(Text)
17. Let our pages exist
First of all we need to make our WikiPage
model available inside the model module:
from example.model.wiki import WikiPage
Then run setup-app again so that our new
table get created.
(tg2env)$ paster setup-app development.ini
18. Time for some magic
from tgext.crud import EasyCrudRestController
class WikiController(EasyCrudRestController):
model = model.WikiPage
class RootController(BaseController):
pages = WikiController(DBSession)
[...]
19. Works out of the box
CRUD did all the work for us, now we just have
to create our wiki pages!
20. Fetch the pages for our menu
_before method get called before calling every
controller method and tmpl_context is the
TurboGears template context, which is always
available inside a request.
from tg import tmpl_context
class RootController(BaseController):
pages = WikiController(DBSession)
def _before(self, *args, **kw):
tmpl_context.pages = DBSession.query(model.WikiPage).all()
21. Let's draw our menu
Inside master.html mainmenu add links for
our pages
<li py:for="page in getattr(tmpl_context, 'pages', [])">
<a href="${'/%s' % page.pagename}">${page.pagename}</a>
</li>
23. Serving our wiki pages
from webob.exc import HTTPNotFound
from webhelpers.markdown import Markdown
class RootController(BaseController):
pages = WikiController(DBSession)
def _before(self, *args, **kw):
_default method gets
tmpl_context.pages = DBSession.query(model.WikiPage).all() executed every time Object
Dispatch is able to resolve
@expose('example.templates.index') the url only up to the
def index(self): controller but is not able to
return dict(page='index') find a method that matches
the url
@expose('example.templates.page')
def _default(self, *args, **kw):
page = None
if args:
page = DBSession.query(model.WikiPage).filter_by(pagename=args[0]).first()
if not page:
raise HTTPNotFound
return dict(page=page.pagename, content=Markdown(page.data).convert())
24. example.templates.page
Very little to do, just show a title and its
content.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<title>${page}</title> Markup is required to tell Genshi that the
</head> value should not be escaped.
<body> webhelpers.Markdown generated nice
<h1>${page}</h1> HTML for us and we want to show the
${Markup(content)} rendering not the HTML itself!
</body>
</html>
25. Woh! It works!
Our website is ready! Now we can add any
number of pages using the Markdown syntax.
26. Have Fun!
TurboGears2, its crud and pluggable
applications can make as easy as 1,2,3 creating
a web application, just open your editor and
have fun!
http://www.turbogears.org