Upload
francisco-ribeiro
View
29.512
Download
2
Embed Size (px)
DESCRIPTION
This presentation shall address the web2py web framework, my favorite way to develop web apps. web2py is a free, open-source web framework for agile development of secure database-driven web applications; it is written in Python and programmable in Python. web2py is a full-stack framework, meaning that it contains all the components you need to build fully functional web applications.Ease of use is the primary goal for web2py. For us, this means reducing the learning and deployment time. This is why web2py is a full-stack framework without dependencies. It requires no installation and has no configuration files. Everything works out of the box, including a web server, database and a web-based IDE that gives access to all the main features. I will show you why web2py can make you more productive by bringing the result of a reflection over the best ideas of the most popular MVC based web frameworks enforcing the best practices for a fast, scalable and secure web application with minimal effort. There will be a live demo where you can get a faster grasp on how does it work and how fun it can be.For more: www.web2py.com
Citation preview
Web2py: Web development like a boss
Francisco Gama Tabanez Ribeiro
Sunday, December 11, 11
Agenda
‣ my path on web dev - lessons learned
‣ web2py web framework:
‣ intro
‣ internals
‣ live demo - building a WiKi
‣ resources
Sunday, December 11, 11
view source
Sunday, December 11, 11
Sunday, December 11, 11
Sunday, December 11, 11
Sunday, December 11, 11
1999 - Lisbon, Portugal
Sunday, December 11, 11
Sunday, December 11, 11
Sunday, December 11, 11
10 1 11 01
010 0
1
Sunday, December 11, 11
5 years later...Sunday, December 11, 11
PHP?
Python is well designed, PHP is not:Although it’s perfectly possible to write good
code in PHP, it’s much easier to write great code in Python
from Why PHP Is Fun and Easy But Python Is Marriage Material
Sunday, December 11, 11
Perl?
• LUKE: Is Perl better than Python?
• YODA: No... no... no. Quicker, easier, more seductive.
• LUKE: But how will I know why Python is better than Perl?
• YODA: You will know. . .
When your code you try to read six months from now.
Sunday, December 11, 11
Ruby?
Beautiful is better than ugly.
[...]
Although practicality beats purity.
from The Zen of Python
Sunday, December 11, 11
God will love you anyway
Heroku
Rubinius
Sunday, December 11, 11
Sunday, December 11, 11
Sunday, December 11, 11
?
Sunday, December 11, 11
?
Sunday, December 11, 11
lessons learned (1/2)
• rapid turnaround
• secure by design
• web development is mostly about visual UI’s
• relies mostly on user interaction & feedback
• convention over configuration
• flexible as in hackable
Sunday, December 11, 11
lessons learned (2/2)
• fast and efficient but mostly scalable and stable
• different things come in different places
• plays well with others
• backward compatibility assured
• live community & support resources
• fun
Sunday, December 11, 11
Sunday, December 11, 11
Welcome to the club!
founder: Massimo Di Pierro
main contributors: +80
users: +2000
Sunday, December 11, 11
Install and run...
Sunday, December 11, 11
Install and run...
curl -O http://www.web2py.com/examples/static/web2py_src.zip
Sunday, December 11, 11
why?• easy to run, install and play with
• python based
• web IDE included
• web based database admin
• no dependencies, no configuration required
• multiple platform (GAE, EC2, Jython...)
Sunday, December 11, 11
why?
• Open source (LGPLv3 license)
• web2py applications have no license constraints
• web2py allows application bytecode compilation
• always backward compatible
• deployment-friendly...
Sunday, December 11, 11
deployment..• always backward
compatible
• integrates well with web servers (cgi, fcgi, mod_python, mod_wsgi)
• error logging and ticketing support
• Database Abstraction Layer integrated
• integrated versioning
• integrated self-update capability
• multiple caching methods
• testing methods and debug shell
Sunday, December 11, 11
what else?• secure (against XSS, Injection flaws, RFI)
• SSL and multiple authentication methods
• enforces good Software Engineering practices (MVC, Server-side form validation, postbacks...)
• Internationalization support
• HTML/XML, RSS/ATOM, RTF, PDF, JSON, AJAX (includes jQuery), XML-RPC, CSV, REST, WIKI, Flash/AMF, and Linked Data (RDF)
Sunday, December 11, 11
Web basedAdmin Interface
Sunday, December 11, 11
Hello World
def hello1(): return "Hello World"
controllers/simple_examples.py
Sunday, December 11, 11
Olá Mundo(Translation)
def hello2(): return T("Hello World")
controllers/simple_examples.py
Sunday, December 11, 11
Controller-View
def hello3(): return dict(message=T("Hello World"))
{{extend 'layout.html'}}<h1>{{=message}}</h1>
controllers/simple_examples.py
views/hello3.html
Sunday, December 11, 11
View
{{extend 'layout.html'}}<h1>{{=message}}</h1>
<html> <head> <title>example</title> </head> <body> {{include}} </body> </html>
views/layout.html
views/hello3.html
Sunday, December 11, 11
Web2py: flow
Sunday, December 11, 11
Web2py: inside
Sunday, December 11, 11
Web2py: inside
Python
Sunday, December 11, 11
Web2py: inside
Python
rocket(SSL enabled web server)
API for third party servers(Apache,...)
Sunday, December 11, 11
Web2py: inside
Python
rocket(SSL enabled web server)
API for third party servers(Apache,...)
Core libraries(HTTP request/response, session,
auth, services, DAL,...)
Contrib(simplejson, markdown, memcache, pyrtf, rss,...)
Sunday, December 11, 11
Web2py: inside
Python
rocket(SSL enabled web server)
API for third party servers(Apache,...)
Core libraries(HTTP request/response, session,
auth, services, DAL,...)
User Applications
admin welcome user app
Contrib(simplejson, markdown, memcache, pyrtf, rss,...)
examples
Sunday, December 11, 11
Web2py: inside
Python
rocket(SSL enabled web server)
API for third party servers(Apache,...)
Core libraries(HTTP request/response, session,
auth, services, DAL,...)
User Applications
admin welcome user app
Contrib(simplejson, markdown, memcache, pyrtf, rss,...)
examples
Sunday, December 11, 11
web2py app inside
Sunday, December 11, 11
web2py app inside
models
controllers
views
translations
static data
plugins & modules
appadmin
data
Sunday, December 11, 11
web2py app inside
models
controllers
views
translations
static data
plugins & modules
appadmin
data
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
SQlite
MySQL
PostgreSQL
Oracle
MSSQL
DB2
Firebird
MyBase
Informix
Google App Engine
Database types:
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
string
textinteger
double
datedatetime
time
booleanpassword
upload
blob
referencelist:string
list:interger
list:reference
Field types:
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
db.recipe.title.requires=IS_NOT_EMPTY()db.recipe.category.requires=IS_IN_DB(db,'category.id','category.name')db.category.name.requires=IS_NOT_IN_DB(db,'category.name')db.recipe.description.default='Fill your recipe in here...'
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
db = SQLDB(‘sqlite://data.db’)
db.define_table(‘category’,Field(‘name’))
db.define_table(‘recipe’,Field(‘title’),Field(‘category’,db.category),Field(‘description’,‘text’))
db.recipe.title.requires=IS_NOT_EMPTY()db.recipe.category.requires=IS_IN_DB(db,'category.id','category.name')db.category.name.requires=IS_NOT_IN_DB(db,'category.name')db.recipe.description.default='Fill your recipe in here...'
IS_DATEIS_DATETIME
IS_DATETIME_IN_RANGEIS_DATE_IN_RANGE
IS_DECIMAL_IN_RANGEIS_EMAIL
IS_EMPTY_ORIS_EQUAL_TO
IS_EXPRIS_FLOAT_IN_RANGE
IS_GENERIC_URLIS_HTTP_URL
IS_IMAGEIS_INT_IN_RANGE
IS_IN_DBIS_IN_SET
IS_IN_SUBSETIS_IPV4
IS_LENGTHIS_LIST_OFIS_LOWERIS_MATCH
IS_NOT_EMPTYIS_NOT_IN_DBIS_NULL_OR
IS_SLUGIS_STRONG
IS_TIMEIS_UPLOAD_FILENAME
IS_UPPERIS_URL
IS_ALPHANUMERIC
Validators:
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
def index(): return dict(recipes=db().select(db.recipe.ALL))
def add(): form=SQLFORM(db.recipe) if form.accepts(request.vars): redirect(URL(r=request,f='index')) return dict(form=form)
def show():recipes=db(db.recipe.id==request.args[0]).select()if not len(recipes):
redirect(URL(r=request,f='index')) return dict(recipe=recipes[0])
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
in file views/default/add.html:{{extend 'layout.html'}}<h1>{{T(‘New recipe’)}}</h1>{{=form}}
in file views/default/index.html:{{extend 'layout.html'}}<h1> {{T(‘List all recipes’)}} </h1><table>{{for recipe in recipes:}}<tr><td>{{=A(T(recipe.title),_href=URL(r=request,f='show', args=recipe.id))}}</td></tr>{{pass}}</table>
in file views/default/show.html:{{extend 'layout.html'}}<h1> {{=T(recipe.title)}} {{=db.recipe.category.represent(recipe.category)}}</h1><pre> {{=recipe.description}}</pre>
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
T(‘New recipe’)
T(‘List all recipes’)
T(recipe.title)
‘Nova receita’‘Nouvelle receité’...
‘Listar receitas’‘Lister receité’...
‘bitoque’‘duple cliché’...
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
images
css
js
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
ways to extend your apps:• modules are good way to import external code• plugins are applications subsets - a small application “inside” your application
web2py app inside
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
data
web2py app inside
• databases (SQlite, MySQL, PostgreSQL, Oracle, MSSQL, DB2, Firebird, MyBase, Informix, Google App Engine)
• metadata for automatic migrations
• cache
Sunday, December 11, 11
models
controllers
views
translations
static data
plugins & modules
appadmin
datadefault web based interface to your data
web2py app inside
Sunday, December 11, 11
web2py URL parsinghttp://www.myhost.com:8000/myapp/default/index.html/a/b/c?name=Max
request.application = “myapp”request.controller = “default”
request.function = “index”request.extension = “html”
request.args = [‘a’,’b’,’c’]request.vars.name = “Max”
Environment variablesare in request.env
equivalent torequest.vars[‘name’]
Sunday, December 11, 11
• functions in controllers return dicts() into views
• controller methods are exposed to views with the same name which is also used in the URL
• input validators (forms) are defined in the model as integrity constraints (requires)
• DAL automatically creates the id field in your tables
• model code has full application scope
remember..
Sunday, December 11, 11
remember..• you can use your editor of choice
• you can use DAL separately
• in controller functions you can redefine the target view with response.view=”theme/myview.html”
• web2py shell is your friend
• recommended - start your files with: # coding: utf8
Sunday, December 11, 11
sessions
<html> <head></head> <body> <h1>{{=message}}</h1> <h2>Number of visits: {{=counter}}</h2> </body></html>
def index(): session.counter = (session.counter or 0) + 1 return dict(message="Hello from MyApp", counter=session.counter)
inside controller:
inside view:
Sunday, December 11, 11
Overview
Sunday, December 11, 11
DAL select()
db(query).select( field1, field2, ..., left=[db.table.on(query)], orderby=field|~field, groupby=field|field limitby=(0,10), cache=(cache.ram,5000))
Examples:rows = db(db.recipe).select()
rows = db(db.recipe.id>0).select(db.recipe.title, db.recipe.category,orderby = db.recipe.category, distinct=True)
# christmas recipes from 2000query1 = db.recipe.created_in.year()>2000query2 = db.recipe_created_in.month()<10rows = db(query1 & query2).select()
Sunday, December 11, 11
DAL select()
db(query).select( field1, field2, ..., left=[db.table.on(query)], orderby=field|~field, groupby=field|field limitby=(0,10), cache=(cache.ram,5000))
Examples:rows = db(db.recipe).select()
rows = db(db.recipe.id>0).select(db.recipe.title, db.recipe.category,orderby = db.recipe.category, distinct=True)
# christmas recipes from 2000query1 = db.recipe.created_in.year()>2000query2 = db.recipe_created_in.month()<10rows = db(query1 & query2).select()
equivalent to: rows = db(db.recipe.id>0).select(db.recipe.ALL)
Sunday, December 11, 11
DAL select()
db(query).select( field1, field2, ..., left=[db.table.on(query)], orderby=field|~field, groupby=field|field limitby=(0,10), cache=(cache.ram,5000))
Examples:rows = db(db.recipe).select()
rows = db(db.recipe.id>0).select(db.recipe.title, db.recipe.category,orderby = db.recipe.category, distinct=True)
# christmas recipes from 2000query1 = db.recipe.created_in.year()>2000query2 = db.recipe_created_in.month()<10rows = db(query1 & query2).select()
equivalent to: rows = db(db.recipe.id>0).select(db.recipe.ALL)
queries can be combined with and(&), or(|) and not(~)
Sunday, December 11, 11
DAL operations• Insert:
db.category.insert(name=‘soup’)
• Update:db(db.category.name==‘soup’).update(name=‘french soups’)
• Delete:db(db.category.name==‘french soups’).delete()
• Count:db(db.category.name.like(‘%soup%’)).count()
Sunday, December 11, 11
DAL operations• Insert:
db.category.insert(name=‘soup’)
• Update:db(db.category.name==‘soup’).update(name=‘french soups’)
• Delete:db(db.category.name==‘french soups’).delete()
• Count:db(db.category.name.like(‘%soup%’)).count()
Other operators:.max(), .min(), .sum(), .belongs(), .like(),...
Sunday, December 11, 11
DAL operations• Insert:
db.category.insert(name=‘soup’)
• Update:db(db.category.name==‘soup’).update(name=‘french soups’)
• Delete:db(db.category.name==‘french soups’).delete()
• Count:db(db.category.name.like(‘%soup%’)).count()
Other operations:transactions, inner joins, left outer joins, nested selects, self-references, many2many, ...
Other operators:.max(), .min(), .sum(), .belongs(), .like(),...
Sunday, December 11, 11
Forms
• SQLFORM() / SQLFORM.factory()
• FORM()
• CRUD()
• <form></form>
Sunday, December 11, 11
HTML helpers (form)def display_form():
form=FORM('Your name:', INPUT(_name='name', requires=IS_NOT_EMPTY()), INPUT(_type='submit')) if form.accepts(request.vars, session): response.flash = form.vars.name +', thank you' elif form.errors: response.flash = 'form has errors' else: response.flash = 'please fill the form' return dict(form=form)
...<div class="flash">{{=response.flash or ''}}</div>{{=form}}...
inside controller:
inside view:
Sunday, December 11, 11
ComponentsLOAD()
{{=LOAD(‘controller’,‘function’, ajax=True)}}
Sunday, December 11, 11
ComponentsLOAD()
{{=LOAD(‘controller’,‘function’, ajax=True)}}
supports auth signatures
Sunday, December 11, 11
web2py Shell
python web2py.py -S myapplication -M
Sunday, December 11, 11
HTML helpers
• BEAUTIFY(whatever)
• URL('application', 'controller', 'function', args=['x', 'y'], vars=dict(z='t'))
• much more... /application/controller/function/x/y?z=t
Sunday, December 11, 11
Authentication & Authorization (Role-based)
Sunday, December 11, 11
Authentication & Authorization (Role-based)doc_id = db.document.insert(body = 'top secret')
agents = auth.add_group(role = 'Secret Agent')
auth.add_membership(agents, james_bond)
auth.add_permission(agents, 'read', secrets)
auth.has_permission('read', secrets, doc_id, james_bond)
auth.has_permission('update', secrets, doc_id, james_bond)
auth.is_logged_in()
Sunday, December 11, 11
Authentication & Authorization (Role-based)
@auth.requires_login()
@auth.requires_membership(agents)
@auth.requires_permission('read', secrets)
@auth.requires_permission('delete', 'any file')
@auth.requires(auth.user_id==1 or request.client=='127.0.0.1')
@auth.requires_permission('add', 'number')
you also have: agents = auth.add_group(role = 'Secret Agent') auth.add_membership(agents, james_bond) auth.add_permission(agents, 'read', secrets)
Sunday, December 11, 11
@[email protected]@service.xmldef list_recipes(): return db(db.recipe).select()
@[email protected]@[email protected]@[email protected]@[email protected]@service.amfrpc3('domain')
Services
http://myhost/application/controller/list_recipes.xml
Sunday, December 11, 11
@[email protected]@service.xmldef list_recipes(): return db(db.recipe).select()
@[email protected]@[email protected]@[email protected]@[email protected]@service.amfrpc3('domain')
Services
http://myhost/application/controller/list_recipes.xml.csv
....json
Sunday, December 11, 11
@[email protected]@service.xmldef list_recipes(): return db(db.recipe).select()
@[email protected]@[email protected]@[email protected]@[email protected]@service.amfrpc3('domain')
Services
http://myhost/application/controller/list_recipes.xml.csv
....json
Sunday, December 11, 11
Errors (ticketing)
errors/exceptions are logged into tickets
Sunday, December 11, 11
Other
• Cron
• Routes
• Plugins
• Modules
• Grids
Caching in functions: @cache("key",cache.ram,5000) def f(): return dict()
Caching actions/views: @cache(request.env.path_info,5000,cache.ram) def action(): return response.render(response.view,dict())
could be:cache.ram, cache.disk, cache.memcache
Sunday, December 11, 11
demoWiki
Sunday, December 11, 11
demoWiKi app(features)
• add pages
• show pages
• edit pages
• versioning
• authentication (local and remote)
• internationalization
• comments (with/without AJAX)
Sunday, December 11, 11
demoWiKi app(model)
Sunday, December 11, 11
web2py in real life
Francisco Costa
Sunday, December 11, 11
Documentation (1/2)
• online book (www.web2py.com/book)
• book (printed version - amzn.to/vzjiqT)
• screencasts (www.vimeo.com)
• interactive examples (default app)
• AlterEgo (FAQ - www.web2py.com/AlterEgo)
Sunday, December 11, 11
Documentation (2/2)
• epydoc (www.web2py.com/examples/static/epydoc/)
• API:(www.web2py.com/book/default/chapter/04#API)
Sunday, December 11, 11
Community (1/2)
• free web2py appliances (www.web2py.com/appliances)
• code snippets (www.web2pyslices.com)
• groups:• users - http://groups.google.com/group/web2py/
• developers - http://groups.google.com/group/web2py-developers
Sunday, December 11, 11
Community (2/2)
• #web2py (IRC channel at irc.freenode.net)
• twitter (http://twitter.com/web2py)
• user voice (http://web2py.uservoice.com)
• web2py websites (www.web2py.com/poweredby)
• web2py plugins (www.web2py.com/plugins/)
Sunday, December 11, 11
Thank you
childish wont-let-go nickname: blackthorne
blackthorne (geek) bthorne_daily (social)
[email protected] (PGP key: 0xBDD20CF1)
http://www.digitalloft.org (homepage)
Sunday, December 11, 11
web2py app defaults• menu
• web2py_ajax (jQuery)
• auth, mail, download and services
• english default lang translation
• generic view
Sunday, December 11, 11