turbogears2 pluggable applications

26
Pluggable Applications with TurboGears2 Using TurboGears2 pluggable applications Alessandro Molina @__amol__

Upload: amol

Post on 27-Jan-2015

111 views

Category:

Technology


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: TurboGears2 Pluggable Applications

Pluggable Applications with TurboGears2

Using TurboGears2 pluggable applications

Alessandro Molina@__amol__

Page 2: TurboGears2 Pluggable Applications

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.

Page 3: TurboGears2 Pluggable Applications

OverView

CONTROLLER class RootController(BaseController): error = ErrorController()

@expose('wiki20.templates.index') def index(self): return dict(page='index')

MODEL class Page(DeclarativeBase): __tablename__ = 'pages' id = Column(Integer, primary_key=True) pagename = Column(Text, unique=True) data = Column(Text)

TEMPLATE <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude">

<xi:include href="master.html" />

<head> <title>My Title</title></head>

<body> <h1>${page}</h1> <div py:for="num in range(10)">${num}</div></body></html>

Page 4: TurboGears2 Pluggable Applications

Object Dispatch

class BlogEntryController(BaseController): @expose() def index(self, post): return 'HI'

@expose() def edit(self, post): return 'HI'

@expose() def update(self, post): return 'HI'

class RootController(BaseController): blog = BlogEntryController()

@expose() def index(self): return 'HI' @expose() def about(self): return 'HI' @expose() def more(self, *args, **kw): return 'HI'

URL CONTROLLER

/index RootController.index

/ RootController.index

/blog/3 BlogEntryController.index(post = 3)

/blog/update?post=3 BlogEntryController.update(post = 3)

/about RootController.about

/more/1/2/3 RootController.more(args[0]=1, args[1]=2, args[3]=3)

/more?data=5 RootController.more(kw['data']=5)

Page 5: TurboGears2 Pluggable Applications

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()

DBSession.add(Page(pagename='index_about', data='About Content'))

DBSession.flush()transaction.commit()

TurboGears does this for us if no errors appeared during the request. The Unit of Work will reorder insertions as required 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.

Page 6: TurboGears2 Pluggable Applications

Genshi XML templating<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/"> <head> <title>Geddit: News</title> </head> <body class="index"> <div id="header"> <h1>News</h1> </div>

<ol py:if="links"> <li py:for="link in reversed(links)"> <a href="${link.url}">${link.title}</a> posted by ${link.username} at ${link.time.strftime('%x %X')} </li> </ol>

<p><a class="action" href="/submit/">Submit new link</a></p>

<div id="footer"> <hr /> <p class="legalese">© 2007 Edgewall Software</p> </div> </body></html>

● Validated, if there is any error in the template structure it will be reported

● XML based, just a bunch of attributes to your

tags, this makes possible to edit the templates with wysiwyg editors

● Conditional tags

● Tags content iteration

● Variables substitution with escaping

● Templates inheritance

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.

Page 7: TurboGears2 Pluggable Applications

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

Page 8: TurboGears2 Pluggable Applications

Explore Quickstart

Page 9: TurboGears2 Pluggable Applications

Sample Project

● Personal Website ● with Photo Galleries ● a Blog ● and many Wiki like pages

Page 10: TurboGears2 Pluggable Applications

Photo Galleries

from tgext.pluggable import plugplug(base_config, 'photos')

<body> <div id="getting_started"> ${h.call_partial('photos.partials:albums')} <div style="clear:both"/> </div></body>

We Plug tgapp-photos to have ready to use photo albums support

Our Template ends being just a call to the partial that renders the previews of our albums

.photos_gallery { display: block; float: left; text-align: center; margin-left: 10px; margin-bottom: 10px; }

Some CSS to give a minimal look to our gallery

(tg2env)$ easy_install tgapp-photos(tg2env)$ mkdir example/public/attachments

Install tgapp-photos pluggable applications for photo albums support

Page 11: TurboGears2 Pluggable Applications

Our Photo galleries

Argh, it look awful...But this is why CSS exist

Page 12: TurboGears2 Pluggable Applications

I want my blog, Now !

from tgext.pluggable import plugplug(base_config, 'photos')

<body> <div id="getting_started"> ${h.call_partial('photos.partials:albums')} <div style="clear:both"/> ${h.call_partial('smallpress.partials:articles')} </div></body>

We Plug tgapp-photos to have ready to use photo albums support

Add call to smallpress partial to render the list of articles of the default blog

(tg2env)$ easy_install tgapp-smallpressInstall tgapp-smallpress for blogs support and create a directory where it can store attachments

#smallpress_articles { margin-top: 30px; }.smallpress_article h2 { margin-bottom: 0; }.smallpress_article h2 a { text-decoration: none; }.smallpress_article h4 { margin-top: 0; }

Our usual bunch of minimal styling.

Page 13: TurboGears2 Pluggable Applications

Well, somehow it works

Page 14: TurboGears2 Pluggable Applications

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!

Page 15: TurboGears2 Pluggable Applications

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.

Page 16: TurboGears2 Pluggable Applications

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, Columnfrom sqlalchemy.types import Unicode, Integer, DateTime, Textfrom 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)

Page 17: TurboGears2 Pluggable Applications

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

Page 18: TurboGears2 Pluggable Applications

Time for some magic

from tgext.crud import EasyCrudRestController class WikiController(EasyCrudRestController): model = model.WikiPage

class RootController(BaseController): pages = WikiController(DBSession) [...]

Page 19: TurboGears2 Pluggable Applications

Works out of the box

CRUD did all the work for us, now we just have to create our wiki pages!

Page 20: TurboGears2 Pluggable Applications

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()

Page 21: TurboGears2 Pluggable Applications

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>

Page 22: TurboGears2 Pluggable Applications

Doh!

Again?!Well our website yet doesn't know how to display our wiki pages

Page 23: TurboGears2 Pluggable Applications

Serving our wiki pagesfrom webob.exc import HTTPNotFoundfrom webhelpers.markdown import Markdownclass RootController(BaseController): pages = WikiController(DBSession)

def _before(self, *args, **kw): tmpl_context.pages = DBSession.query(model.WikiPage).all()

@expose('example.templates.index') def index(self): return dict(page='index')

@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())

_default method gets executed every time Object Dispatch is able to resolve the url only up to the controller but is not able to find a method that matches the url

Page 24: TurboGears2 Pluggable Applications

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> </head> <body> <h1>${page}</h1> ${Markup(content)} </body></html>

Markup is required to tell Genshi that the value should not be escaped. webhelpers.Markdown generated nice HTML for us and we want to show the rendering not the HTML itself!

Page 25: TurboGears2 Pluggable Applications

Woh! It works!

Our website is ready! Now we can add any number of pages using the Markdown syntax.

Page 26: TurboGears2 Pluggable Applications

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