web2py:Web development like a boss

31,698 views

Published on

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

Published in: Technology
0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
31,698
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
309
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

web2py:Web development like a boss

  1. 1. Web2py: Web development like a boss Francisco Gama Tabanez Ribeiro
  2. 2. Agenda ‣ my path on web dev - lessons learned ‣ web2py web framework: ‣ intro ‣ internals ‣ live demo - building a WiKi ‣ resources
  3. 3. view source
  4. 4. 1999 - Lisbon, Portugal
  5. 5. 1 0 1 11 01 01 0 0 1
  6. 6. 5 years later...
  7. 7. 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
  8. 8. 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.
  9. 9. Ruby? Beautiful is better than ugly. [...] Although practicality beats purity. from The Zen of Python
  10. 10. ?
  11. 11. ?
  12. 12. 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
  13. 13. 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
  14. 14. Welcome to the club! founder: Massimo Di Pierro main contributors: +80 users: +2000
  15. 15. Install and run...
  16. 16. Install and run... curl -O http://www.web2py.com/examples/static/web2py_src.zip
  17. 17. 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...)
  18. 18. why? • Open source (LGPLv3 license) • web2py applications have no license constraints • web2py allows application bytecode compilation • always backward compatible • deployment-friendly...
  19. 19. 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
  20. 20. 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)
  21. 21. Web based Admin Interface
  22. 22. Hello World def hello1(): return "Hello World" controllers/simple_examples.py
  23. 23. Olá Mundo (Translation) def hello2(): return T("Hello World") controllers/simple_examples.py
  24. 24. Controller-View def hello3(): return dict(message=T("Hello World")) {{extend 'layout.html'}} <h1>{{=message}}</h1> controllers/simple_examples.py views/hello3.html
  25. 25. View {{extend 'layout.html'}} <h1>{{=message}}</h1> <html> <head> <title>example</title> </head> <body> {{include}} </body> </html> views/ views/hello3.html
  26. 26. Web2py: flow
  27. 27. Web2py: inside
  28. 28. Web2py: inside Python
  29. 29. Web2py: inside Python rocket (SSL enabled web server) API for third party servers (Apache,...)
  30. 30. 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,...)
  31. 31. 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
  32. 32. 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
  33. 33. web2py app inside
  34. 34. web2py app inside models controllers views translations static data plugins & modules appadmin data
  35. 35. web2py app inside models controllers views translations static data plugins & modules appadmin data
  36. 36. models controllers views translations static data plugins & modules appadmin data web2py app inside
  37. 37. 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
  38. 38. 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
  39. 39. 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
  40. 40. 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 text integer double date datetime time boolean password upload blob reference list:string list:interger list:reference Field types: web2py app inside
  41. 41. 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
  42. 42. 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
  43. 43. 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_DATE IS_DATETIME IS_DATETIME_IN_RANGE IS_DATE_IN_RANGE IS_DECIMAL_IN_RANGE IS_EMAIL IS_EMPTY_OR IS_EQUAL_TO IS_EXPR IS_FLOAT_IN_RANGE IS_GENERIC_URL IS_HTTP_URL IS_IMAGE IS_INT_IN_RANGE IS_IN_DB IS_IN_SET IS_IN_SUBSET IS_IPV4 IS_LENGTH IS_LIST_OF IS_LOWER IS_MATCH IS_NOT_EMPTY IS_NOT_IN_DB IS_NULL_OR IS_SLUG IS_STRONG IS_TIME IS_UPLOAD_FILENAME IS_UPPER IS_URL IS_ALPHANUMERIC Validators: web2py app inside
  44. 44. models controllers views translations static data plugins & modules appadmin data web2py app inside
  45. 45. 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
  46. 46. 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='sh ow', 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
  47. 47. 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
  48. 48. models controllers views translations static data plugins & modules appadmin data images css js web2py app inside
  49. 49. 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
  50. 50. 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
  51. 51. models controllers views translations static data plugins & modules appadmin data default web based interface to your data web2py app inside
  52. 52. web2py URL parsing http://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 variables are in request.env equivalent to request.vars[‘name’]
  53. 53. • 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..
  54. 54. 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
  55. 55. 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:
  56. 56. Overview
  57. 57. 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 2000 query1 = db.recipe.created_in.year()>2000 query2 = db.recipe_created_in.month()<10 rows = db(query1 & query2).select()
  58. 58. 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 2000 query1 = db.recipe.created_in.year()>2000 query2 = db.recipe_created_in.month()<10 rows = db(query1 & query2).select() equivalent to: rows = db(db.recipe.id>0).select(db.recipe.ALL)
  59. 59. 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 2000 query1 = db.recipe.created_in.year()>2000 query2 = db.recipe_created_in.month()<10 rows = 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(~)
  60. 60. 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()
  61. 61. 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(), .bel ongs(), .like(),...
  62. 62. 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(), .bel ongs(), .like(),...
  63. 63. Forms • SQLFORM() / SQLFORM.factory() • FORM() • CRUD() • <form></form>
  64. 64. 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:
  65. 65. Components LOAD() {{=LOAD(‘controller’,‘function’, ajax=True)}}
  66. 66. Components LOAD() {{=LOAD(‘controller’,‘function’, ajax=True)}} supports auth signatures
  67. 67. web2py Shell python web2py.py -S myapplication -M
  68. 68. HTML helpers • BEAUTIFY(whatever) • URL('application', 'controller', 'function', args=['x', 'y'], vars=dict(z='t')) • much more... /application/controller/function/x/y?z=t
  69. 69. Authentication & Authorization (Role-based)
  70. 70. 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()
  71. 71. 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)
  72. 72. @service.csv @service.rss @service.xml def 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
  73. 73. @service.csv @service.rss @service.xml def 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
  74. 74. @service.csv @service.rss @service.xml def 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
  75. 75. Errors (ticketing) errors/exceptions are logged into tickets
  76. 76. 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
  77. 77. demoWiki
  78. 78. demoWiKi app (features) • add pages • show pages • edit pages • versioning • authentication (local and remote) • internationalization • comments (with/without AJAX)
  79. 79. demoWiKi app (model)
  80. 80. web2py in real life Francisco Costa
  81. 81. 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)
  82. 82. Documentation (2/2) • epydoc (www.web2py.com/examples/static/epydoc/) • API: (www.web2py.com/book/default/chapter/ 04#API)
  83. 83. 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
  84. 84. 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/)
  85. 85. Thank you childish wont-let-go nickname: blackthorne blackthorne (geek) bthorne_daily (social) francisco@ironik.org (PGP key: 0xBDD20CF1) http://www.digitalloft.org (homepage)
  86. 86. web2py app defaults • menu • web2py_ajax (jQuery) • auth, mail, download and services • english default lang translation • generic view

×