Tutorial: - Simon Pami©s
Transcript of Tutorial: - Simon Pami©s
Tutorial: repoze.bfg An introduction to Web-Development
About and Topics Simon Pamiés
Background B. Sc. BioInformatics and Genome Research M.A. IT-Management (Business Analytics)
Work banality design & communication Senior Application Engineer and Consultant Planning and Development of Web-Applications
Technologies (excerpt) Zope, Plone, repoze.bfg, J2EE, GWT, jQuery, MongoDB, RabbitMQ
Basics
URL Mapping
Views
Templates
Persistence
Authentication
Authorization
About this presentation
Configuration scheme used here is mostly declarative
Presentation refers to repoze.bfg version 1.2.x
Examples imply having a nested package structure with at least browser/ model/ tests/
URL mapping is mostly done using dispatching
repoze.bfg – Introduction
Framework to ease development of web applications
Small code and memory footprint
Uses WSGI protocol
Very well documented
Easy to start with
Goals
Simplicity (parsimonious feature set)
Speed (e.g. no regexps on url matching)
Extensibility
„Like Zope but less“
„Like Pylons but more“
Blather
repoze.bfg is built upon the WebOb library ... that provides request and response base classes ... that handles encoding and decoding of data (e.g. POST)
repoze.bfg is built to play nicely with WSGI ... protocol that connects a web server and application together ... notion of a „pipeline“ where request goes through
repoze.bfg is also known as BFG (Big Fine Gun, ...)
It doesn‘t do
Persistence
Session
Indexing
Restricted code execution
Authentication
...
repoze.bfg vs.
Zope 2+3 (ZTK, BlueBream, ...) Big, Needs some amount of cognitive load to be understood
Grok Easy to start with, hard to continue (much magic)
Django Full-stacked (makes assumptions about the persistence layer)
Pylons Lacks features like object traversal
URL Mapping
URLs are entry points to your web app
Each URL (including parametrized ones) leads to one specific response of your application e.g. /login, /dashboard, /member/profile, /member/preferences
An URL is composed of different segments and parameters e.g. /A/B/C or /A/B/SomeAction?param=value
Key-Question for every web application
How to map URLs to code?
Routing
Routing takes the whole URL (including defined parametrized segments) and tries to find a matching rule (the most specific one)
/logout /member/12345/profile /member/12345/profile/messages /actions/some-ajax-query?param=value
Routing (Example)
<route path = „/member/:uid/logout“ name = „logout-handler“ view = „.user.logoutHandler“ />
def logoutHandler(context, request): useruid = request.matchdict.get(‚uid‘) return webob.Response(„User has been logged out“)
Traversal
Traversal automagically maps each segment to objects
Walks the object graph
Context is set according to the last segment matching an object
Useful in applications where you have dynamic object hierarchies (CMS)
Traversal (Example)
URL
OBJECT GRAPH
B is the last object matching the route Z is the view name B is passed to the view as context
Traversal (Example)
<view name = „edit“ context = „.models.folder“ view = „.browser.folder.editHandler“ />
def editHandler(context, request): # context is a folder return webob.Response(„Some HTML to edit a folder“)
Views
Methods or classes that act as URL targets
Work on request object containing all relevant information
Context provides object that links to model
View always returns webob.Response
View (Method based)
def requestHandler(context, request): # do some fancy stuff return webob.Response(„body content here“)
context is not required but if present holds model related data
request is an object containing all relevant data
request.params, request.GET, request.POST
request[„HTTP_REFERER“]
View (Class based)
class UserRelatedActionsView(object):
def __init__(self, context, request): self.context = context self.request = request
def __call__(self): return webob.Response()
Using a class you can group and reuse functionality
Mainly used for templates to provide tool like interface
If in doubt use methods
Templates
Using Python to build HTML documents sucks
Use a templating language (e.g. Chameleon ZPT) Full ZPT implementation Template compiler In-place expression evaluation supported
<html> <b tal:condition=„context.hasTitle()“> ${context.Title()} </b>
</html>
Combining Views and Templates
class LoginView(object):
def isAuthenticated(self): return ‚repoze.who.userid‘ in self.request
def __call__(self): return {‚param‘ : True} # arbitrary parameters here
<b tal:condition=„not view.isAuthenticated() and param“> Please login using the form below
</b>
<route name=„login-form“ view=„.login.LoginView“ renderer=„templates/login_form.pt“ />
Combining Views and Templates
class LoginView(object):
def __call__(self): from repoze.bfg.chameleon_zpt import render[...] return render_template_to_response( ‚templates/login_form.pt‘, view=self, request=self.request, param=True)
<b tal:condition=„not view.isAuthenticated() and param“> Please login using the form below
</b>
Renderer
Built-in renderers available String JSON Chameleon Templates
Easy to write your own renderer
Lessons learned?
repoze.bfg filling the gap between other frameworks
Notion of URL mapping
Traversal
Routing
Views (Method vs. Class)
Templates
What next?
Persisting data (model) – you‘re free to choose where ZODB RDBMS „No-SQL“ (MongoDB, ...)
Security Authenticating users (repoze.who) ACLs (repoze.bfg, repoze.what)
Practical part Installation (buildout, paster), examples
Persisting data
Two easy steps Write your model using plain python objects Attach a persistent data handler
Your code should not know anything about the storage
Model Plain Python object using values from internal state (__dict__)
Handler At transaction boundaries (e.g. request) or upon app request
persists data from object to database or the way round
The Model
From your_db_handler import Persistent
class Person(Persistent):
def __init__(self, name): self.name = name
The base class takes care of all operations related to the attached storage
Your class just computes data based on values from known attributes (e.g. name)
The handler (just to get the idea)
class Persistent(object):
def save(self): if self.isNew(): self.connector.create(self.__dict__) else: self.connector.update(self.__dict__)
def delete(self): self.connector.delete(self.uid)
Batteries included (ZODB Handler)
ZODB gives you full fledged object oriented database transaction awareness transparent model handling scalability
ZODB can be included with just a few lines of code into your app – and it just works™
Include ZODB into your model „from persistent import Persistent“
Security (Authentication)
Application needs to authenticate user
persist authentication
return state of authentication
Authenticating: Basic Auth, Form, SSPI
Persisting: Cookie, Session, URL token
State: Variable in request
repoze.who
Framework to handle authentication Well thought architecture
Easily extensible
Rich set of predefined handlers
„Who is coming in?“
repoze.who (cont.)
Four stages where code can plug in Classifier (What type of request do we have?) Challenger (How to get auth data?) Authenticator (How to check wheter data is valid?) Persister (How to save valid data?)
Simple configuration
Many plugins available Cookie Auth, Basic Auth, Facebook Auth, OAuth
repoze.who configuration
[general] request classifier and challenge decider
[identifiers] extract data from request
[authenticators] authenticates user based on data from identifiers
[challengers] get user login data (basic auth, form based)
Security (Authorization)
Authorization asks „User Bob has permission Edit?“
User –(1:n)–> Group –(1:m)–> Permissions
Protecting Code (e.g. getTitle() method ) Views and URLs (e.g. /person/create-form)
repoze.bfg has simple authorization handling included
I recommend against using repoze.what (way to complicated)
Authorization using repoze.bfg
Use authentication policy to define groups for users e.g. repozewho1authenticationpolicy
Implement or use authorization policy to check for rights e.g. aclauthorizationpolicy
Practical Part: What?
Simple „Hello World“ application Wiring a view with a template Handling form submission (POST)
Another simple application (address book) Creating a model (person) User can create persons Persons are listed and can be deleted
Practical Part: Installation
Checking setuptools $ easy_install http://peak.telecommunity.com/dist/ez_setup.py
Creating a virtualenv environment $ virtualenv –no-site-packages repozebfg
Installing repoze.bfg $ bin/easy_install repoze.bfg==1.2.1
Things to understand Virtualenv Setuptools
PP: Setting up a project
Creating a simple project structure $ bin/paster create -t bfg_starter helloworld
Things to understand PasteScript Python Packages and Eggs
PP: Starting up the application
Fire up server $ bin/paster serve –reload helloworld.ini
Things to understand WSGI Python WSGI reference implementation PasteDeploy framework
PP: Next steps
Understanding the structure helloworld.ini run.py configure.zcml views.py
Writing some code Creating a form Validating form Saving data