Web2py: Quick Reference

Web2py is an open source web framework for rapid, secure and scalable development of web based application, written in python. It is based on MVC(Model View Controller) concept.

Complete Reference available at web2py book

Quick Overview

If you have used web2py, this short reference could be helpful.
If you are a newbie, there are better references elsewhere.

Components

Each application has the following components:

  • models - describes databases
  • controllers - application logic and workflow
  • views - presentation of controllers
  • languages - how to translate strings into supported languages
  • static - static files used in rendering
  • errors - storing error reports
  • sessions - store information related to user
  • databases - tables
  • cache - store cached application items
  • modules - other optional python modules
  • private - files internaly accessed by controllers
  • uploads - files uploaded by users

Workflow

workflow

  • Controller function() name should match View/function.[extension]
  • Files under Model are executed in alphabetical order. Variables defined in model are accessible to controller
  • While looking for action, all functions in controller are executed

When an application is created using admin interface (which itself is a web2py application), default.py and appadmin.py controllers are created.

CLI

  • Start web2py
$ ###  reuses the first time password set 
$ python web2py.py -a "<recycle>" 
  • Run python interactive mode
$ python web2py.py -S appname/controller
. . . 
>>> help(HTTP)

>>> dir(HTTP)
['class', 'delattr', 'dict', 'doc', 'format', 'getattribute', 'getitem', 'getslice', 'hash', 'init', 'module', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'setstate', 'sizeof', 'str', 'subclasshook', 'unicode', 'weakref', 'args', 'cookies2headers', 'message', 'to']
>>> 

Core

  • gluon.storage.Storage object's attributes can be accessed by object.attribute or object['attribute']

request object

InstanceDescriptionObject Type
request.cookies Cookies as passed in HTTP request Dict
request.env environment variables including HTTP header variables. gluon.storage.Storage
request.application name of the requested application String
request.controller name of the requested controller String
request.function action name or the function name in the controller String
request.extension defaults to "html". String
request.folder application directory in absolute path String
request.now current time during request datetime.datetime
request.utcnow current time in UTC datetime.datetime
request.args URL path following controller function name list
request.vars HTTP GET / POST query variables gluon.storage.Storage
request.get_vars HTTP GET query variables gluon.storage.Storage
request.post_vars HTTP POST query variables gluon.storage.Storage
request.client client ip String
request.is_local is client localhost Boolean
request.is_https is HTTPS? Boolean
request.body read only file stream file stream
request.ajax is AJAX? Boolean
request.cid id of component generated by AJAX Integer
request.requires_https() if HTTP stop code and execution and redirect to httpsNone
request.user_agent() parses user agent and returns information Dict

response object

InstanceDescriptionObject Type
response.cookies cookies sent by the server Dict
response.download(request, db, [ chunk_size = , attachments = [True|False] ]) download of uploaded files, needs arg as encoded filename as stored in db.
response.files list of .css, .js, coffee and .less files required by page gets automatically linked in layout.html List
response.flash notify user popup String
response.headers HTTP response headers Dict
response.menu rendered by MENU helper list of list/tuples
response.meta meta information response.meta.{author,description,keywords} Storage
response.session_id id of current session. DONT CHANGE Integer
response.session_id_name name of the session cookie String
response.status HTTP status code
response.toolbar debugging purpose that can be used in view
response._vars accessible only in view String
response.optimize_css [ concat|minify|inline ]
response.optimize_js [ concat|minify|inline ]
response.view name of the view template "%s/%s.%s" % (request.controller, request.function, request.extension) String
response.delimiters delimiter to be used in view ('``', '@@') Set

Secure cookie and httponly

response.cookies[response.session_id_name]['httponly'] = True
response.cookies[response.session_id_name]['secure'] = True

session

InstanceDescriptionObject Type
session.anyvariable set any variable in session Storage
session.forget(response) dont save session
session.secure() set session cookie to be secure cookie (for https)
session.connect(request, response, db, masterapp = None, cookie_key = '', compression_level = None ) store session in db, masterapp for SSO

cache

Cache output of function in memory or disk.

@cache.action(request.env.path_info, 
				time_expire = <duration to cache seconds>, 
				cache_model = [cache.disk|cache.ram], 
				prefix = "string", 					# easy to cache.ram.clear(prefix*)
				session = False,
				vars = True,
				lang = True,
				user_agent = False,
				public = True,
				valid_statuses = None ,				#	[List]
				quick = 'SVP' )						 
                
# quick - Session, Vars, Lang, User_agent, Public  (first letter in capitals)                 

URL

Generate internal URL

InstanceDescription
URL('f') path_info - application/controller/f
URL('c','f') path_info - application/c/f
URL('a', 'c', 'f') path_info - a/c/f
URL('f', args = ['av', 'p'], vars = dict(var=12)) application/controller/f/av/p?var=12
URL('static', 'image.png') application/static/image.png
URL('static', 'images/icons/arrow.png') application/static/images/icons/arrow.png (subfolder static special condition)
URL('static', 'bootstrap.min', extension = 'css') overwride default extension or set extension = False to disable
URL('f', scheme = True , host = "website.com", port = 8000) generate absolute urls ://website.com:8000/f
URL('f', hmac_key = "", salt = "", hash_vars = dict(key=123)) adds _signature to the url

Cookies

  • Adding a new cookie
response.cookies['newcookie'] = "values"
response.cookies['newcookie']['expires'] = 3600
response.cookies['newcookie']['path]' = "/" 

Routes

# only takes care of visiting main site. 
default_application = "appname"   	


# Pattern based routing 
routes_in = (
				('/formd', '/wiki/default/formd'),
				('/user', '/wiki/default/user'),
				)
routes_out = [(x, y) for (y, x) in routes_in]

# routing on error
routes_onerror = [ 
					('appname/*' , '/appname/default/errormsg'),
					('appname/500' , '/appname/default/errormsg'),
					( '*/500' , '/appname/default/errormsg'),
					('*/*', '/appname/default/errm') ]

Views

Conditions and loops

# end block with pass
# one liners dont need pass 

# for loop 
{{for key in request.keys(): }}
Request Name : {{=key}} - {{=request[key]}} <br/>
{{pass}}

# while loop 
{{
k=3 
while k > 0 :}}
<li>{{=B(k)}}</li>
{{k = k - 1 
pass}}


# if elif else 
{{if request.client == "127.0.0.1" : 
=A("Localboy", _href=URL('local'))
else:
=A("Remote", _href=URL('remote'))
pass}}


#using functions 
{{def itemize(link):
return LI(A(link,_href=URL(link)) }}

HTML Helpers

A, B, BODY, BR, CENTER, CODE, COL, COLGROUP,
DIV, EM, EMBED, FIELDSET, FORM, H1, H2, H3, H4, H5, H6,
HEAD, HR, HTML, I, IFRAME, IMG, INPUT, LABEL, LEGEND,
LI, LINK, OL, UL, META, OBJECT, OPTION, P, PRE,
SCRIPT, OPTGROUP, SELECT, SPAN, STYLE,
TABLE, TAG, TD, TEXTAREA, TH, THEAD, TBODY, TFOOT,
TITLE, TR, TT, URL, XHTML, xmlescape, embed64

CAT, MARKMIN, MENU, ON

DAL

When playing around table schematics, you may confront several issues. I had to use one of the following in different cases:-

  • truncate table content using db.tablename.truncate()
  • drop by db.tablename.drop() and recreate
  • empty folder databases

Create table

db.define_table('tablename', 
					Field('name', 
					'type' , 					# string | password | upload | integer | boolean | text | list:(string|reference|integer) | blob | double | date | time 
					length = None ,					# length of the string 
					default = "value " ,				# default value to be shown in SQLFORM 
					required = False, 				# no insert allowed this field at DAL
					requires = <validator>, 			# SQLFORM needs to satisfy validator 
					ondelete = 'CASCADE', 				# (CASCADE|NO ACTION|SET NULL) what to do with reference fields 
					notnull = True ,				# translates into NOT NULL SQL , at DAL 
					unique = True , 				# UNIQUE SQL, at DAL
					label = "Label " ,				# label to be put at SQLFORM
					comment = "Comment ", 				# Comment to be displayed below input field in SQLFORM
					writable = True , 				# whether SQLFORM will show this field while insert/update 
					readable = True , 				# whether SQLFORM will show this field while select
					update = True , 				# whether SQLFORM will show this field while update 
					compute = function, 				# call func with row as dictionary, and return value that is to be inserted 
					blob = True, 					# base64 of data 
					format = "%(name)s" ))				# this implies, other tables referencing this, will have entry as "name" and not id. 
		

SQL

db.tablename.insert(name="Danny")				# returns id 
db.tablename.bulk_insert([dict]) 				# returns list of ids

db.commit()							# force commit transaction, otherwise depends on page execution
db.rollback()							# rollback transaction 

db.executesql("-- run any sql statment")			# run sql 

db._lastsql							# prints last run sql code 

db.executesql('create index if not exists idxname on tablename(name);')		# only way to manage indexes 


### select all rows with name Danny
db(db.tablename.name == "Danny").select(db.tablename.ALL) 					

### select name like "%anny" and display id, name 
db(db.tablename.name.like("%anny")).select(db.tablename.id, db.tablename.name) 		


db(db.tablename).select(db.tablename.id, db.tablename.name, orderby = db.tablename.name) 	# select 2 columns,  sort by name 
                      orderby =~ db.tablename.name 		# reverse order search 
                      groupby = db.tablename.groupname
                      distinct = True
                      having = db.person.id > 5 
                      limitby = (0 ,2 ) 			# from 0 to  1     


## Group by and count
## eg. for table data group by column type and show count 
[ [r.data.type , r._extra('count(data.type)') ]  for r in db().select(db.data.type,  'count(data.type)', groupby = db.data.type)  ]


## Logical operators & / | /  ~   => and / or / not 
# building query gradually 
query = db.t.name != 'Alice'						
query &= db.t.id > 5 
query |=db.t.groupname == 'Admin' 


db(db.t.id > 5).count()							# count records 
db(db.t.id > 5).isempty()						# True if no records 
db(db.t.id > 5).delete()						# delete if condition is true 
db(db.t.id == 5 ).update(name = 'bob', groupname = "webdev")		# update entries based on condition 


db().select(db.t.ALL, db.ot.ALL, left = db.ot.on(db.ot.id == db.t.id))	# outerjoin 

db().select(db.t.id == db.ot.id ).select()				# inner join 

db(db.t.name.like('%anny')).select()					# fetch name like '%anny'
db(db.t.name.like('%anny', case_sensitive = True)).select()		# case sensitive search  (default not case sensitive)
db(db.t.name.startswith('Da').select()					# name starting with 
db(db.t.name.contains('ann').select()					# string containing in name 

# name containing (all|one) of the strings in list , depending on all = True | False 
db(db.t.name.contains(['Da','nn'], all = True).select()			

# change case to upper and search 
db(db.t.name.upper().contains('ann').select()				

# fetch year part of the field time. day(), hour(), month(), minutes(), seconds()
db(db.d.time.year() == 2009 ).select()					

# sum of all records. avg() / min() / max() / len() - length of string/text/boolean
db.d.value.sum()							

# prefix '_'  for looking at sql generated 
print db.tablename._insert(...)						


Validators

ValidatorDescription
IS_ALPHANUMERIC(error_message = "not valid") is alphanumeric
IS_DATE(format=('%Y-%m-%d'), error_message ="error ") is date as mentioned format
IS_DATE_IN_RANGE(format=T('%Y-%m-%d'), check date range [ minimum=datetime.date(2008,1,1), maximum=datetime.date(2009,12,31), error_message='must be YYYY-MM-DD!') ]
IS_DATETIME(format=T('%Y-%m-%d %H:%M:%S')) is datetime
IS_DATETIME_IN_RANGE(format=T('%Y-%m-%d %H:%M:%S'), is datetime in range [ minimum=datetime.datetime(2008,1,1,10,30), maximum=datetime.datetime(2009,12,31,11,45), error_message='must be YYYY-MM-DD HH:MM::SS!') ]
IS_DECIMAL_IN_RANGE(0, 10, dot=".") 0.0 to 10.0
IS_EMAIL(error_message = 'invalid email') check if mail entered
IS_EQUAL_TO(request.vars.password, error_message = 'passwords dont match') check if equal to another value
IS_EXPR('int(value)%3==0') check if expression evaluates to True
IS_FLOAT_IN_RANGE(0, 100, dot=".") 0 to 100
IS_INT_IN_RANGE(0, 100) int in range
IS_IN_SET(['a', 'b', 'c'],zero=T('choose one')) within range, while choosing show "choose one" as default
IS_LENGTH(maxsize =255, [minsize = 10]) string length check
IS_LOWER() check if string is in lower case
IS_MATCH('\d{5}$') match RE
IS_NOT_EMPTY() check if not empty
IS_URL(allowed_schemes=['https'], prepend_scheme='https') check if valid url
IS_STRONG(min=10, special = 2 , upper =2 ) check pass strength , min length , number of special chars, number of upper chars
IS_IMAGE(extensions=('jpeg','png), maxsize=(200,200)) allow extensions with pixel limits
IS_IPV4() check ipv4
IS_UPPER() check upper
IS_EMPTY_OR() either empty or another validator IS_EMPTY_OR(IS_DATE())
CLEANUP() never fails, but removes characters whose ASCII codes not in [10,13,32-127]
CRYPT() performs HMAC algo with 1000 iterations on data,
IS_NOT_IN_DB(db, 'table.field') check if not in this db
IS_IN_DB(db, 'table.id', '%(name)s', zero = "choose") in Form drop down, we dont see id, but name from reference table. Default option - "choose"
ANY_OF([IS_IPV4(), IS_URL(prepend_scheme='http')]) either of ipv4 or url

Hope this article was useful, in some way.
Cheers!

Dinesh Gunasekar - | Tags : Web Application, Web2py
comments powered by Disqus