created by Massimo Di Pierro
Here are some working and complete examples that explain the basic syntax of the framework. You can click on the web2py keywords (in the highlighted code!) to get documentation.
In controller: simple_examples.py
def hello1():
return "Hello World"
If the controller function returns a string, that is the body of the rendered page.
In controller: simple_examples.py
def hello2():
return T("Hello World")
The function T() marks strings that need to be translated. Translation dictionaries can be created at /admin/default/design
In controller: simple_examples.py
def hello3():
return dict(message=T("Hello World"))
and view: simple_examples/hello3.html
{{extend 'layout.html'}}
<h1>{{=message}}</h1>
If you return a dictionary, the variables defined in the dictionery are visible to the view (template).
In controller: simple_examples.py
def hello4():
response.view='simple_examples/hello3.html'
return dict(message=T("Hello World"))
You can change the view, but the default is /[controller]/[function].html. If the default is not found web2py tries to render the page using the generic.html view.
In controller: simple_examples.py
def hello5():
return HTML(BODY(H1(T('Hello World'),_style="color: red;"))).xml()
You can also generate HTML using helper objects HTML, BODY, H1, etc. Each of these tags is an class and the views know how to render the corresponding objects. The method .xml() serializes them and produce html/xml code for the page. Each tag, DIV for example, takes three types of arguments:
unnamed arguments, they correspond to nested tags
named arguments and name starts with ‘_’. These are mapped blindly into tag attributes and the ‘_’ is removed. attributes without value like “READONLY” can be created with the argument “_readonly=ON”.
named arguments and name does not start with ‘_’. They have a special meaning. See “value=” for INPUT, TEXTAREA, SELECT tags later.
In controller: simple_examples.py
def status():
return dict(request=request,session=session,response=response)
Here we are showing the request, session ad response objects using the generic.html template.
In controller: simple_examples.py
def redirectme():
redirect(URL(r=request,f='hello3'))
You can do redirect.
In controller: simple_examples.py
def raisehttp():
raise HTTP(400,"internal error")
You can raise HTTP exceptions to return an error page.
In controller: simple_examples.py
1
def raiseexception():
1/0
return 'oops'
If an exception occurs (other than HTTP) a ticket is generated and the event is logged for the administrator. These tickets and logs can be accessed, reviewed and deleted and any later time.
In controller: simple_examples.py
def servejs():
import gluon.contenttype
response.headers['Content-Type']=\
gluon.contenttype.contenttype('.js')
return 'alert("This is a Javascript document");'
You can serve other than HTML pages by changing the contenttype via the response.headers. The gluon.contenttype module can help you figure the type of the file to be server. NOTICE: this is not necessary for static files unless you want to require authorization.
In controller: simple_examples.py
def makejson():
import gluon.contrib.simplejson as sj
return sj.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
If you are into Ajax, web2py includes gluon.contrib.simplejson, developed by Bob Ippolito. This module provides a fast and easy way to serve asynchronous content to your Ajax page. gluon.simplesjson.dumps(...) can serialize most Python types into JSON. gluon.contrib.simplejson.loads(...) performs the reverse operation.
In controller: simple_examples.py
def makertf():
import gluon.contrib.pyrtf as q
doc=q.Document()
section=q.Section()
doc.Sections.append(section)
section.append('Section Title')
section.append('web2py is great. '*100)
response.headers['Content-Type']='text/rtf'
return q.dumps(doc)
web2py also includes gluon.contrib.pyrtf, developed by Simon Cusack and revised by Grant Edwards. This module allows you to generate Rich Text Format documents including colored formatted text and pictures.
In controller: simple_examples.py
def rss_aggregator():
import datetime
import gluon.contrib.rss2 as rss2
import gluon.contrib.feedparser as feedparser
d = feedparser.parse("http://rss.slashdot.org/Slashdot/slashdot/to")
rss = rss2.RSS2(title=d.channel.title,
link = d.channel.link,
description = d.channel.description,
lastBuildDate = datetime.datetime.now(),
items = [
rss2.RSSItem(
title = entry.title,
link = entry.link,
description = entry.description,
# guid = rss2.Guid('unkown'),
pubDate = datetime.datetime.now()) for entry in d.entries]
)
response.headers['Content-Type']='application/rss+xml'
return rss2.dumps(rss)
web2py includes gluon.contrib.rss2, developed by Dalke Scientific Software, which generates RSS2 feeds, and gluon.contrib.feedparser, developed by Mark Pilgrim, which collects RSS and ATOM feeds. The above controller collects a slashdot feed and makes new one.
In controller: simple_examples.py
from gluon.contrib.markdown import WIKI
def ajaxwiki():
form=FORM(TEXTAREA(_id='text'),INPUT(_type='button',_value='markdown',
_onclick="ajax('ajaxwiki_onclick',['text'],'html')"))
return dict(form=form,html=DIV(_id='html'))
def ajaxwiki_onclick():
return WIKI(request.vars.text).xml()
web2py also includes gluon.contrib.markdown (markdown2) which converts WIKI markup to HTML following this syntax. In this example we added a fancy ajax effect.
In controller: session_examples.py
def counter():
if not session.counter: session.counter=0
session.counter+=1
return dict(counter=session.counter)
and view: session_examples/counter.html
{{extend 'layout.html'}}
<h1>session counter</h1>
<h2>{{for i in range(counter):}}{{=i}}...{{pass}}</h2>
<a href="{{=URL(r=request)}}">click me to count</a>
Click to count. The session.counter is persistent for this user and application. Every applicaiton within the system has its own separate session management.
In controller: template_examples.py
def variables(): return dict(a=10, b=20)
and view: template_examples/variables.html
{{extend 'layout.html'}}
<h1>Your variables</h1>
<h2>a={{=a}}</h2>
<h2>a={{=b}}</h2>
A view (also known as template) is just an HTML file with {{...}} tags. You can put ANY python code into the tags, no need to indent but you must use pass to close blocks. The view is transformed into a python code and then executed. {{=a}} prints a.xml() or escape(str(a)).
In controller: template_examples.py
def test_for(): return dict()
and view: template_examples/test_for.html
<h1>For loop</h1>
{{for number in ['one','two','three']:}}
<h2>{{=number.capitalize()}}<h2>
{{pass}}
You can do for and while loops.
In controller: template_examples.py
def test_if(): return dict()
and view: template_examples/test_if.html
{{extend 'layout.html'}}
<h1>If statement</h1>
{{a=10}}
{{if a%2==0:}}
<h2>{{=a}} is even</h2>
{{else:}}
<h2>{{=a}} is odd</h2>
{{pass}}
You can do if, elif, else.
In controller: template_examples.py
def test_try(): return dict()
and view: template_examples/test_try.html
{{extend 'layout.html'}}
<h1>Try... except</h1>
{{try:}}
<h2>a={{=1/0}}</h2>
{{except:}}
infinity</h2>
{{pass}}
You can do try, except, finally.
In controller: template_examples.py
def test_def(): return dict()
and view: template_examples/test_def.html
{{extend 'layout.html'}}
{{def itemlink(name):}}<li>{{=A(name,_href=name)}}</li>{{return}}
<ul>
{{itemlink('http://www.google.com')}}
{{itemlink('http://www.yahoo.com')}}
{{itemlink('http://www.nyt.com')}}
</ul>
You can write functions in HTML too.
In controller: template_examples.py
def escape(): return dict(message='<h1>text is scaped</h1>')
and view: template_examples/escape.html
{{extend 'layout.html'}}
<h1>Strings are automatically escaped</h1>
<h2>Message is</h2>
{{=message}}
The argument of {{=...}} is always escaped unless it is an object with a .xml() method such as link, A(...), a FORM(...), a XML(...) block, etc.
In controller: template_examples.py
def xml():
return dict(message=XML('<h1>text is not escaped</h1>'))
and view: template_examples/xml.html
{{extend 'layout.html'}}
<h1>XML</h1>
<h2>Message is</h2>
{{=message}}
If you do not want to esacpe the argument of {{=...}} mark it as XML.
In controller: template_examples.py
def beautify(): return dict(message=BEAUTIFY(request))
and view: template_examples/beautify.html
{{extend 'layout.html'}}
<h1>BEAUTIFY</h1>
<h2>Message is</h2>
{{=message}}
You can use BEUTIFY to turn lists and dictionaries into organized HTML.
In controller: layout_examples.py
def civilized():
response.menu=[['civilized',True,URL(r=request,f='civilized')],
['slick',False,URL(r=request,f='slick')],
['basic',False,URL(r=request,f='basic')]]
response.flash='you clicked on civilized'
return dict(message="you clicked on civilized")
and view: layout_examples/civilized.html
{{extend 'layout_examples/layout_civilized.html'}}
<h2>{{=message}}</h2>
<p>{{for i in range(1000):}}bla {{pass}}</p>
You can specify the layout file at the top of your view. civilized Layout file is a view that somewhere in the body contains {{include}}.
In controller: layout_examples.py
def slick():
response.menu=[['civilized',False,URL(r=request,f='civilized')],
['slick',True,URL(r=request,f='slick')],
['basic',False,URL(r=request,f='basic')]]
response.flash='you clicked on slick'
return dict(message="you clicked on slick")
and view: layout_examples/slick.html
{{extend 'layout_examples/layout_sleek.html'}}
<h2>{{=message}}</h2>
{{for i in range(1000):}}bla {{pass}}
Same here, but using a different template.
In controller: layout_examples.py
def basic():
response.menu=[['civilized',False,URL(r=request,f='civilized')],
['slick',False,URL(r=request,f='slick')],
['basic',True,URL(r=request,f='basic')]]
response.flash='you clicked on basic'
return dict(message="you clicked on basic")
and view: layout_examples/basic.html
{{extend 'layout.html'}}
<h2>{{=message}}</h2>
{{for i in range(1000):}}bla {{pass}}
‘layout.html’ is the default template, every applicaiton has a copy of it.
In controller: form_examples.py
def form():
form=FORM(TABLE(TR("Your name:",INPUT(_type="text",_name="name",
requires=IS_NOT_EMPTY())),
TR("Your email:",INPUT(_type="text",_name="email",
requires=IS_EMAIL())),
TR("Admin",INPUT(_type="checkbox",_name="admin")),
TR("Sure?",SELECT('yes','no',_name="sure",
requires=IS_IN_SET(['yes','no']))),
TR("Profile",TEXTAREA(_name="profile",
value="write something here")),
TR("",INPUT(_type="submit",_value="SUBMIT"))))
if form.accepts(request.vars,session):
response.flash="form accepted!"
else:
response.flash="form is invalid!"
return dict(form=form,vars=form.vars)
You can use HTML helpers like FORM, INPUT, TEXTAREA, OPTION, SELECT to build forms. the “value=” attribute sets the initial value of the field (works for TEXTAREA and OPTION/SELECT too) and the requires attribute sets the validators. FORM.accepts(..) trys to validate the form and, on success, stores vars into form.vars. On failure the error messages are stored into form.errors and shown in the form.
You can find more examples of the web2py ORM here
Let’s create a simple model with users, dogs, products and purchases (the database of an animal store). Users can have many dogs (ONE TO MANY), can buy many producs and every product can have many buyers (MANY TO MANY).
in model: dba.py
dba=SQLDB('sqlite://tests.db')
dba.define_table('users',
SQLField('name'),
SQLField('email'))
# ONE (users) TO MANY (dogs)
dba.define_table('dogs',
SQLField('owner_id',dba.users),
SQLField('name'),
SQLField('type'),
SQLField('vaccinated','boolean',default=False),
SQLField('picture','upload',default=''))
dba.define_table('products',
SQLField('name'),
SQLField('description','blob'))
# MANY (users) TO MANY (products)
dba.define_table('purchases',
SQLField('buyer_id',dba.users),
SQLField('product_id',dba.products),
SQLField('quantity','integer'))
purchased=((dba.users.id==dba.purchases.buyer_id)&(dba.products.id==dba.purchases.product_id))
dba.users.name.requires=IS_NOT_EMPTY()
dba.users.email.requires=[IS_EMAIL(), IS_NOT_IN_DB(dba,'users.email')]
dba.dogs.owner_id.requires=IS_IN_DB(dba,'users.id','users.name')
dba.dogs.name.requires=IS_NOT_EMPTY()
dba.dogs.type.requires=IS_IN_SET(['small','medium','large'])
dba.purchases.buyer_id.requires=IS_IN_DB(dba,'users.id','users.name')
dba.purchases.product_id.requires=IS_IN_DB(dba,'products.id','products.name')
dba.purchases.quantity.requires=IS_INT_IN_RANGE(0,10)
Tables are created if they do not exist (try... except). Here “purchased” is an SQLQuery object, “dba(purchased)” would be a SQLSet obejcts. A SQLSet object can be selected, updated, deleted. SQLSets can also be intersected. Allowed field types are string, integer, password, text, blob, upload, date, time, datetime, references(*), and id(*). The id field is there by default and must not be declared. references are for one to many and many to many as in the example above. For strings you should specify a length or you get length=32.
You can use dba.tablename.fieldname.requires= to set restrictions on the field values. These restrictions are automatically converted into widgets when generating forms from the table with SQLFORM(dba.tablename).
define_tables creates the table and attempts a migration if table has changed or if database name has changed since last time. If you know you already have the table in the database and you do not want to attemt a migration add one last argument to define_table migrate=False.
In controller: database_examples.py
response.menu=[['Register User',False,URL(r=request,f='register_user')],
['Register Dog',False,URL(r=request,f='register_dog')],
['Register Product',False,URL(r=request,f='register_product')],
['Buy product',False,URL(r=request,f='buy')]]
def register_user():
### create an insert form from the table
form=SQLFORM(dba.users)
### if form correct perform the insert
if form.accepts(request.vars,session):
response.flash='new record inserted'
### and get a list of all users
records=SQLTABLE(dba().select(dba.users.ALL))
return dict(form=form,records=records)
and view: database_examples/register_user.html
{{extend 'layout_examples/layout_civilized.html'}}
<h1>User registration form</h1>
{{=form}}
<h2>Current users</h2>
{{=records}}
This is a simple user registration form. SQLFORM takes a table and returns the corresponding entry form with validators, etc. SQLFORM.accepts is similar to FORM.accepts but, if form is validated, the corresponding insert is also performed. SQLFORM can also do update and edit if a record is passed as its second argument. SQLTABLE instead turns a set of records (result of a select) into an HTML table with links as specified by its optional parameters. The response.menu on top is just a variable used by the layout to make the navigation menu for all functions in this controller.
In controller: database_examples.py
def register_dog():
form=SQLFORM(dba.dogs)
if form.accepts(request.vars,session):
response.flash='new record inserted'
download=URL(r=request,f='download') # to see the picture
records=SQLTABLE(dba().select(dba.dogs.ALL),upload=download)
return dict(form=form,records=records)
and view: database_examples/register_dog.html
{{extend 'layout_examples/layout_civilized.html'}}
<h1>Dog registration form</h1>
{{=form}}
<h2>Current dogs</h2>
{{=records}}
Here is a dog registration form. Notice that the “image” (type “upload”) field is rendered into a <INPUT type=”file”> html tag. SQLFORM.accepts(...) handles the upload of the file into the uploads/ folder.
In controller: database_examples.py
def register_product():
form=SQLFORM(dba.products)
if form.accepts(request.vars,session):
response.flash='new record inserted'
records=SQLTABLE(dba().select(dba.products.ALL))
return dict(form=form,records=records)
and view: database_examples/register_product.html
{{extend 'layout_examples/layout_civilized.html'}}
<h1>Product registration form</h1>
{{=form}}
<h2>Current products</h2>
{{=records}}
Nothing new here.
In controller: database_examples.py
def buy():
form=FORM(TABLE(TR("Buyer id:",INPUT(_type="text",
_name="buyer_id",requires=IS_NOT_EMPTY())),
TR("Product id:",INPUT(_type="text",
_name="product_id",requires=IS_NOT_EMPTY())),
TR("Quantity:",INPUT(_type="text",
_name="quantity",requires=IS_INT_IN_RANGE(1,100))),
TR("",INPUT(_type="submit",_value="Order"))))
if form.accepts(request.vars,session):
### check if user is in the database
if len(dba(dba.users.id==form.vars.buyer_id).select())==0:
form.errors.buyer_id="buyer not in database"
### check if product is in the database
if len(dba(dba.products.id==form.vars.product_id)\
.select())==0:
form.errors.product_id="product not in database"
### if no errors
if len(form.errors)==0:
### get a list of same purchases by same user
purchases=dba(
(dba.purchases.buyer_id==form.vars.buyer_id)&
(dba.purchases.product_id==form.vars.product_id)\
).select()
### if list contains a record, update that record
if len(purchases)>0:
purchases[0].update_record(quantity=\
purchases[0].quantity+form.vars.quantity)
### or insert a new record in table
else:
dba.purchases.insert(buyer_id=form.vars.buyer_id,
product_id=form.vars.product_id,
quantity=form.vars.quantity)
response.flash="product purchased!"
if len(form.errors): response.flash="invalid valus in form!"
### now get a list of all purchases
records=dba(purchased).select(dba.users.name, \
dba.purchases.quantity,dba.products.name)
return dict(form=form,records=SQLTABLE(records),
vars=form.vars,vars2=request.vars)
and view: database_examples/buy.html
{{extend 'layout_examples/layout_civilized.html'}}
<h1>Purchase form</h1>
{{=form}}
[ {{=A('reset purchased',_href=URL(r=request,f='reset_purchased'))}}
| {{=A('delete purchased',
_href=URL(r=request,f='delete_purchased'))}} ]<br/>
<h2>Current purchases (SQL JOIN!)</h2>
<p>{{=records}}</p>
Here is a rather sophisticated buy form. It checks that the buyer and the product are in the database and updates the corresponding record or inserts a new purchase. It also does a JOIN to list all purchases.
In controller: database_examples.py
def delete_purchased():
dba(dba.purchases.id>0).delete()
redirect(URL(r=request,f='buy'))
In controller: database_examples.py
def reset_purchased():
dba(dba.purchases.id>0).update(quantity=0)
redirect(URL(r=request,f='buy'))
This is an update on an SQLSet. (dba.purchase.id>0 identifies the set containing only table dba.purchases.)
In controller: database_examples.py
def download():
import gluon.contenttype
filename=request.args[0]
response.headers['Content-Type']=\
gluon.contenttype.contenttype(filename)
return open('applications/%s/uploads/%s' %
(request.application,filename),'rb').read()
This controller allows users to download the uploaded pictures of the dogs. Remember the upload=URL(...’download’...) statement in the register_dog function. Notice that in the URL path /application/controller/function/a/b/etc a, b, etc are passed to the controller as request.args[0], request.args[1], etc. Since the URL is validated request.args[] always contain valid filenames and no ‘~’ or ‘..’ etc. This is usefult to allow visitors to link uploaded files.
In controller: cache_examples.py
def cache_in_ram():
import time
t=cache.ram('time',lambda:time.ctime(),time_expire=5)
return dict(time=t,link=A('click to reload',_href=URL(r=request)))
The output of lambda:time.ctime() is cached in ram for 5 seconds. The string ‘time’ is used as cache key.
In controller: cache_examples.py
def cache_on_disk():
import time
t=cache.disk('time',lambda:time.ctime(),time_expire=5)
return dict(time=t,link=A('click to reload',_href=URL(r=request)))
The output of lambda:time.ctime() is cached on disk (using the shelve module) for 5 seconds.
In controller: cache_examples.py
def cache_in_ram_and_disk():
import time
t=cache.ram('time',lambda:cache.disk('time',\
lambda:time.ctime(),time_expire=5),time_expire=5)
return dict(time=t,link=A('click to reload',_href=URL(r=request)))
The output of lambda:time.ctime() is cached on disk (using the shelve module) and then in ram for 5 seconds. web2py looks in ram first and if not there it looks on disk. If it is not on disk it calls the function. This is useful in a multiprocess type of environment. The two times do not have to be the same.
In controller: cache_examples.py
@cache(request.env.path_info,time_expire=5,cache_model=cache.ram)
def cache_controller_in_ram():
import time
t=time.ctime()
return dict(time=t,link=A('click to reload',_href=URL(r=request)))
Here the entire controller (dictionary) is cached in ram for 5 seconds. The result of a select cannot be cached unless it is first serialized into a table lambda:SQLTABLE(dba().select(dba.users.ALL)).xml(). You can read below for an even better way to do it.
In controller: cache_examples.py
@cache(request.env.path_info,time_expire=5,cache_model=cache.disk)
def cache_controller_on_disk():
import time
t=time.ctime()
return dict(time=t,link=A('click to reload',_href=URL(r=request)))
Here the entire controller (dictionary) is cached on disk for 5 seconds. This will not work if the dictionary contains unpickleble objects.
In controller: cache_examples.py
@cache(request.env.path_info,time_expire=5,cache_model=cache.ram)
def cache_controller_and_view():
import time
t=time.ctime()
d=dict(time=t,link=A('click to reload',_href=URL(r=request)))
return response.render(d)
response.render(d) renders the dictionary inside the controller, so everything is cached now for 5 seconds. This is best and fastest way of caching!
In controller: cache_examples.py
def cache_db_select():
import time
dba.users.insert(name='somebody',email='gluon@mdp.cti.depaul.edu')
records=dba().select(dba.users.ALL,cache=(cache.ram,5))
if len(records)>20: dba(dba.users.id>0).delete()
return dict(records=records)
The results of a select are complex unpickable objects that cannot be cached using the previous method, but the select command takes an argument cache=(cache_model,time_expire) and will cache the result of the query accordingly. Notice that the key is not necessary since key is generated based on the database name and the select string.
In controller: ajax_examples.py
def index():
return dict()
def data():
if not session.m or len(session.m)==10: session.m=[]
if request.vars.q: session.m.append(request.vars.q)
session.m.sort()
return TABLE(*[TR(v) for v in session.m]).xml()
In view: ajax_examples/index.html
{{extend 'layout.html'}}
<p>Type something and press the button. The last 10 entries will appear sorted in a table below.</p>
<form>
<INPUT type="text" id='q' value="web2py"/>
<INPUT type="button" value="submit"
onclick="ajax('{{=URL(r=request,f='data')}}',['q'],'target');"/>
</form>
<br/>
<div id="target"></div>
The javascript function “ajax” is provided in “web2py_ajax.html” and included by “layout.html”. It takes three arguments, a url, a list of ids and a target it. When called it send to the url (via a get) the values of the ids and display the respose in the value (of innerHTML) of the target id.
In controller: ajax_examples.py
def flash():
response.flash='this text should appear!'
return dict()
In controller: ajax_examples.py
def fade():
return dict()
In view: ajax_examples/fade.html
{{extend 'layout.html'}}
<form>
<input type="button" onclick="fade('test',-0.2);" value="fade down"/>
<input type="button" onclick="fade('test',+0.2);" value="fade up"/>
</form>
<div id="test">{{='Hello World '*100}}</div>
Using the Python doctest notation it is possible to write tests for all controller functions. Tests are then run via the administrative interface which generates a report. Here is an example of a test in the code:
def index():
'''
This is a docstring. The following 3 lines are a doctest:
>>> request.vars.name='Max'
>>> index()
{'name': 'Max'}
'''
return dict(name=request.vars.name)
It is very easy in web2py to stream large files. Here is an example of a controller that does so:
def streamer():
return response.stream(open('largefile.mpg4','rb'),chunk_size=4096)
Web2py has native support for the XMLRPC protocol. Below is a controller function “handler” that exposes two functions, “add” and “sub” via XMLRPC. The controller “tester” executes the two function remotely via xmlrpc.
def add(a,b): return a+b
def sub(a,b): return a-b
def handler(): return response.xmlrpc(request,[add,sub])
def tester():
import xmlrpclib
server=xmlrpclib.ServerProxy('http://hostname:port/app/controller/handler')
return str(server.add(3,4)+server.sub(3,4))
关于本评注系统
本站使用上下文关联的评注系统来收集反馈信息。不同于一般对整章做评注的做法, 我们允许你对每一个独立的“文本块”做评注。一个“文本块”看起来是这样的:
一个“文本块”是一个段落,一个列表项,一段代码,或者其他一小段内容。 你选中它会高亮度显示:
要对文本块做评注,你只需要点击它旁边的标识块:
我们会仔细阅读每个评论,如果可能的话我们也会把评注考虑到未来的版本中去:
如果你愿意你的评注被采用,请确保留下你的全名 (注意不是昵称或简称)
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.extlibrary. Thanks also to Yahoo for YUI itself.