web2py:Web development like a boss

Post on 09-May-2015

29.511 views 2 download

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

Transcript of web2py:Web development like a boss

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

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

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

@service.csv@service.rss@service.xmldef list_recipes(): return db(db.recipe).select()

@service.run@service.csv@service.rss@service.json@service.jsonrpc@service.xml@service.xmlrpc@service.soap@service.amfrpc3('domain')

Services

http://myhost/application/controller/list_recipes.xml

Sunday, December 11, 11

@service.csv@service.rss@service.xmldef list_recipes(): return db(db.recipe).select()

@service.run@service.csv@service.rss@service.json@service.jsonrpc@service.xml@service.xmlrpc@service.soap@service.amfrpc3('domain')

Services

http://myhost/application/controller/list_recipes.xml.csv

....json

Sunday, December 11, 11

@service.csv@service.rss@service.xmldef list_recipes(): return db(db.recipe).select()

@service.run@service.csv@service.rss@service.json@service.jsonrpc@service.xml@service.xmlrpc@service.soap@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)

francisco@ironik.org (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