SlideShare a Scribd company logo
Pluggable Applications with
      TurboGears2
  Using TurboGears2 pluggable applications




                 Alessandro Molina
                    @__amol__
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.
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)
Object Dispatch
                                                       URL                     CONTROLLER
class BlogEntryController(BaseController):
  @expose()
  def index(self, post):
    return 'HI'                              /index                RootController.index
 @expose()
 def edit(self, post):
   return 'HI'                               /                     RootController.index
 @expose()
 def update(self, post):
   return 'HI'                               /blog/3               BlogEntryController.index
                                                                   (post = 3)
class RootController(BaseController):
  blog = BlogEntryController()               /blog/update?post=3   BlogEntryController.update
                                                                   (post = 3)
  @expose()
  def index(self):
    return 'HI'                              /about                RootController.about

 @expose()
 def about(self):
                                             /more/1/2/3           RootController.more
   return 'HI'
                                                                   (args[0]=1, args[1]=2, args[3]=3)
 @expose()
 def more(self, *args, **kw):                /more?data=5          RootController.more
   return 'HI'
                                                                   (kw['data']=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.
Genshi XML templating
<!DOCTYPE html>                                                     ●    Validated, if there is any error in the template
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:py="http://genshi.edgewall.org/">
                                                                         structure it will be reported
 <head>
  <title>Geddit: News</title>                                       ●    XML based, just a bunch of attributes to your
 </head>
 <body class="index">                                                    tags, this makes possible to edit the
  <div id="header">                                                      templates with wysiwyg editors
   <h1>News</h1>
  </div>
                                                                    ●    Conditional tags
  <ol py:if="links">
   <li py:for="link in reversed(links)">
    <a href="${link.url}">${link.title}</a>                         ●    Tags content iteration
    posted by ${link.username} at ${link.time.strftime('%x %X')}
   </li>
  </ol>
                                                                    ●    Variables substitution with escaping

  <p><a class="action" href="/submit/">Submit new link</a></p>      ●    Templates inheritance
  <div id="footer">
    <hr />
    <p class="legalese">© 2007 Edgewall Software</p>
  </div>
 </body>
</html>                                                            Not so fast, if you want superfast templating
                                                                   TurboGears offers Mako support builtin.
                                                                   Mako is not validated and xml editors friendly but it
                                                                   is superfast.
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
Explore Quickstart
Sample Project

● Personal Website

● with Photo Galleries

● a Blog

● and many Wiki like pages
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;
 }
Our Photo galleries




        Argh, it look awful...
       But this is why CSS exist
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; }
Well, somehow it works
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!
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.
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)
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
Time for some magic

from tgext.crud import EasyCrudRestController

class WikiController(EasyCrudRestController):
  model = model.WikiPage

class RootController(BaseController):
  pages = WikiController(DBSession)
  [...]
Works out of the box




CRUD did all the work for us, now we just have
to create our wiki pages!
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()
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>
Doh!




Again?!
Well our website yet doesn't know how to
display our wiki pages
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())
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>
Woh! It works!




Our website is ready! Now we can add any
number of pages using the Markdown syntax.
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

More Related Content

PDF
Django
PDF
Using RequireJS with CakePHP
DOC
How to migrate Cakephp 1.x to 2.x
PDF
Rails 3: Dashing to the Finish
ODP
Zend Framework 1.9 Setup & Using Zend_Tool
PDF
Rails 3 overview
PDF
OSCON Google App Engine Codelab - July 2010
PDF
Laravel 로 배우는 서버사이드 #5
Django
Using RequireJS with CakePHP
How to migrate Cakephp 1.x to 2.x
Rails 3: Dashing to the Finish
Zend Framework 1.9 Setup & Using Zend_Tool
Rails 3 overview
OSCON Google App Engine Codelab - July 2010
Laravel 로 배우는 서버사이드 #5

What's hot (20)

PDF
Getting Into Drupal 8 Configuration
PDF
Drupal 8 Services And Dependency Injection
PDF
Getting to The Loop - London Wordpress Meetup July 28th
PPTX
WordPress plugin #2
PPTX
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
PDF
Angular JS blog tutorial
PDF
#31.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자국비지원...
PPTX
IndexedDB - Querying and Performance
PPTX
Using of TDD practices for Magento
PPTX
Python Code Camp for Professionals 3/4
PPTX
Python Code Camp for Professionals 4/4
PDF
The Best (and Worst) of Django
PDF
Sane Async Patterns
PDF
Alfredo-PUMEX
PPTX
Making Magento flying like a rocket! (A set of valuable tips for developers)
PDF
RicoLiveGrid
PPTX
Django web framework
PPT
JavaScript
PPTX
Python Code Camp for Professionals 1/4
PDF
WebGUI Developers Workshop
Getting Into Drupal 8 Configuration
Drupal 8 Services And Dependency Injection
Getting to The Loop - London Wordpress Meetup July 28th
WordPress plugin #2
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
Angular JS blog tutorial
#31.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자국비지원...
IndexedDB - Querying and Performance
Using of TDD practices for Magento
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 4/4
The Best (and Worst) of Django
Sane Async Patterns
Alfredo-PUMEX
Making Magento flying like a rocket! (A set of valuable tips for developers)
RicoLiveGrid
Django web framework
JavaScript
Python Code Camp for Professionals 1/4
WebGUI Developers Workshop
Ad

Viewers also liked (6)

PDF
OSCON 2004: XML and Apache
PPTX
Rohit maitri dhrumil_namrata
PPTX
Fort Mc Murray Hotel Group
PPTX
Ways to enhance your social media for nonprofits
DOCX
Insurance 2 written
PDF
Developing secure software using Aspect oriented programming
OSCON 2004: XML and Apache
Rohit maitri dhrumil_namrata
Fort Mc Murray Hotel Group
Ways to enhance your social media for nonprofits
Insurance 2 written
Developing secure software using Aspect oriented programming
Ad

Similar to TurboGears2 Pluggable Applications (20)

PPTX
WordPress Structure and Best Practices
PPS
Actionview
PDF
Django Vs Rails
PDF
Mojolicious
PDF
Introduction to backbone presentation
PPTX
lec 14-15 Jquery_All About J-query_.pptx
PPT
Play!ng with scala
PDF
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
PPTX
Flask – Python
PDF
Refresh Austin - Intro to Dexy
PPTX
Grails Advanced
PDF
Web applications with Catalyst
PDF
Django Rest Framework and React and Redux, Oh My!
PDF
Introduction to angular js
PPT
Backbone.js
PPTX
Javascript first-class citizenery
PDF
Modular Test-driven SPAs with Spring and AngularJS
PDF
Rails vs Web2py
PDF
Ejb3 Struts Tutorial En
PDF
Ejb3 Struts Tutorial En
WordPress Structure and Best Practices
Actionview
Django Vs Rails
Mojolicious
Introduction to backbone presentation
lec 14-15 Jquery_All About J-query_.pptx
Play!ng with scala
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Flask – Python
Refresh Austin - Intro to Dexy
Grails Advanced
Web applications with Catalyst
Django Rest Framework and React and Redux, Oh My!
Introduction to angular js
Backbone.js
Javascript first-class citizenery
Modular Test-driven SPAs with Spring and AngularJS
Rails vs Web2py
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial En

More from Alessandro Molina (17)

PDF
PyCon Ireland 2022 - PyArrow full stack.pdf
PDF
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
PDF
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
PDF
EuroPython 2015 - Storing files for the web is not as straightforward as you ...
PDF
PyConIT6 - MAKING SESSIONS AND CACHING ROOMMATES
PDF
PyConIT6 - Messing up with pymongo for fun and profit
PDF
PyConFR 2014 - DEPOT, Story of a file.write() gone wrong
PDF
PyConUK 2014 - PostMortem Debugging and Web Development Updated
PDF
Reactive & Realtime Web Applications with TurboGears2
PDF
Post-Mortem Debugging and Web Development
PDF
MongoTorino 2013 - BSON Mad Science for fun and profit
PDF
PyConUK2013 - Validated documents on MongoDB with Ming
PDF
EuroPython 2013 - FAST, DOCUMENTED AND RELIABLE JSON BASED WEBSERVICES WITH P...
PDF
EuroPython 2013 - Python3 TurboGears Training
PDF
PyGrunn2013 High Performance Web Applications with TurboGears
PDF
Rapid Prototyping with TurboGears2
PDF
From SQLAlchemy to Ming with TurboGears2
PyCon Ireland 2022 - PyArrow full stack.pdf
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EuroPython 2015 - Storing files for the web is not as straightforward as you ...
PyConIT6 - MAKING SESSIONS AND CACHING ROOMMATES
PyConIT6 - Messing up with pymongo for fun and profit
PyConFR 2014 - DEPOT, Story of a file.write() gone wrong
PyConUK 2014 - PostMortem Debugging and Web Development Updated
Reactive & Realtime Web Applications with TurboGears2
Post-Mortem Debugging and Web Development
MongoTorino 2013 - BSON Mad Science for fun and profit
PyConUK2013 - Validated documents on MongoDB with Ming
EuroPython 2013 - FAST, DOCUMENTED AND RELIABLE JSON BASED WEBSERVICES WITH P...
EuroPython 2013 - Python3 TurboGears Training
PyGrunn2013 High Performance Web Applications with TurboGears
Rapid Prototyping with TurboGears2
From SQLAlchemy to Ming with TurboGears2

Recently uploaded (20)

PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
NewMind AI Monthly Chronicles - July 2025
DOCX
The AUB Centre for AI in Media Proposal.docx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Approach and Philosophy of On baking technology
PDF
Modernizing your data center with Dell and AMD
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Machine learning based COVID-19 study performance prediction
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Network Security Unit 5.pdf for BCA BBA.
“AI and Expert System Decision Support & Business Intelligence Systems”
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Mobile App Security Testing_ A Comprehensive Guide.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
NewMind AI Monthly Chronicles - July 2025
The AUB Centre for AI in Media Proposal.docx
Understanding_Digital_Forensics_Presentation.pptx
Review of recent advances in non-invasive hemoglobin estimation
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Approach and Philosophy of On baking technology
Modernizing your data center with Dell and AMD
MYSQL Presentation for SQL database connectivity
Chapter 3 Spatial Domain Image Processing.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Machine learning based COVID-19 study performance prediction

TurboGears2 Pluggable Applications

  • 1. Pluggable Applications with TurboGears2 Using TurboGears2 pluggable applications Alessandro Molina @__amol__
  • 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)
  • 4. Object Dispatch URL CONTROLLER class BlogEntryController(BaseController): @expose() def index(self, post): return 'HI' /index RootController.index @expose() def edit(self, post): return 'HI' / RootController.index @expose() def update(self, post): return 'HI' /blog/3 BlogEntryController.index (post = 3) class RootController(BaseController): blog = BlogEntryController() /blog/update?post=3 BlogEntryController.update (post = 3) @expose() def index(self): return 'HI' /about RootController.about @expose() def about(self): /more/1/2/3 RootController.more return 'HI' (args[0]=1, args[1]=2, args[3]=3) @expose() def more(self, *args, **kw): /more?data=5 RootController.more return 'HI' (kw['data']=5)
  • 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.
  • 6. Genshi XML templating <!DOCTYPE html> ● Validated, if there is any error in the template <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/"> structure it will be reported <head> <title>Geddit: News</title> ● XML based, just a bunch of attributes to your </head> <body class="index"> tags, this makes possible to edit the <div id="header"> templates with wysiwyg editors <h1>News</h1> </div> ● Conditional tags <ol py:if="links"> <li py:for="link in reversed(links)"> <a href="${link.url}">${link.title}</a> ● Tags content iteration posted by ${link.username} at ${link.time.strftime('%x %X')} </li> </ol> ● Variables substitution with escaping <p><a class="action" href="/submit/">Submit new link</a></p> ● Templates inheritance <div id="footer"> <hr /> <p class="legalese">© 2007 Edgewall Software</p> </div> </body> </html> Not so fast, if you want superfast templating TurboGears offers Mako support builtin. Mako is not validated and xml editors friendly but it is superfast.
  • 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
  • 9. Sample Project ● Personal Website ● with Photo Galleries ● a Blog ● and many Wiki like pages
  • 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; }
  • 11. Our Photo galleries Argh, it look awful... But this is why CSS exist
  • 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>
  • 22. Doh! Again?! Well our website yet doesn't know how to display our wiki pages
  • 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