Simple Examples
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.
Example 1
In controller: simple_examples.py
1. 2. | def hello1(): return "Hello World" |
If the controller function returns a string, that is the body of the rendered page.
Try it here: hello1
Example 2
In controller: simple_examples.py
1. 2. | 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
Try it here: hello2
Example 3
In controller: simple_examples.py
1. 2. | def hello3(): return dict(message=T("Hello World")) |
and view: simple_examples/hello3.html
1. 2. 3. 4. | {{extend 'layout.html'}} <h1>{{=message}}</h1>
|
If you return a dictionary, the variables defined in the dictionary are visible to the view (template).
Try it here: hello3
Actions can also be be rendered in other formats like JSON, hello3.json, and XML, hello3.xml
Example 4
In controller: simple_examples.py
1. 2. 3. | 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.
Try it here: hello4
Example 5
In controller: simple_examples.py
1. 2. | def hello5(): return HTML(BODY(H1(T('Hello World'),_style="color: red;"))).xml() # .xml to serialize |
You can also generate HTML using helper objects HTML, BODY, H1, etc. Each of these tags is a class and the views know how to render the corresponding objects. The method .xml() serializes them and produces 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.
Try it here: hello5
Example 6
In controller: simple_examples.py
1. 2. 3. | def hello6(): response.flash=T("Hello World in a flash!") return dict(message=T("Hello World")) |
response.flash allows you to flash a message to the user when the page is returned. Use session.flash instead of response.flash to display a message after redirection. With default layout, you can click on the flash to make it disappear.
Try it here: hello6
Example 7
In controller: simple_examples.py
1. 2. | def status(): return dict(toobar=response.toolbar()) |
Here we are showing the request, session and response objects using the generic.html template.
Example 8
In controller: simple_examples.py
You can do redirect.
Try it here: redirectme
Example 9
In controller: simple_examples.py
1. 2. | def raisehttp(): raise HTTP(400,"internal error") |
You can raise HTTP exceptions to return an error page.
Try it here: raisehttp
Example 10
In controller: simple_examples.py
1. 2. 3. | 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 at any later time.
Example 11
In controller: simple_examples.py
1. 2. 3. 4. | def servejs(): import gluon.contenttype response.headers['Content-Type']=gluon.contenttype.contenttype('.js') return 'alert("This is a Javascript document, it is not supposed to run!");' |
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 served. NOTICE: this is not necessary for static files unless you want to require authorization.
Try it here: servejs
Example 12
In controller: simple_examples.py
1. 2. | def makejson(): return response.json(['foo', {'bar': ('baz', None, 1.0, 2)}]) |
If you are into Ajax, JSON is fully supported in web2py. Providing a fast and easy way to serve asynchronous content to your Ajax page. Response.json can serialize most Python types into JSON.
Try it here: makejson
New in web2py 1.63: Any normal action returning a dict is automatically serialized in JSON if '.json' is appended to the URL.
Example 13
In controller: simple_examples.py
1. 2. 3. 4. 5. 6. 7. 8. 9. | 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.
Try it here: makertf
In controller: simple_examples.py
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. | 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.
Try it here: rss_aggregator
Example 15
In controller: simple_examples.py
1. 2. 3. 4. 5. 6. 7. 8. | def ajaxwiki(): form=FORM(TEXTAREA(_id='text',_name='text'), INPUT(_type='button',_value='markmin', _onclick="ajax('ajaxwiki_onclick',['text'],'html')")) return dict(form=form,html=DIV(_id='html'))
def ajaxwiki_onclick(): return MARKMIN(request.vars.text).xml() |
The markmin wiki markup is described here.
web2py also includes gluon.contrib.markdown.WIKI helper (markdown2) which converts WIKI markup to HTML following this syntax. In this example we added a fancy ajax effect.
Try it here: ajaxwiki
Session Examples
Example 16
In controller: session_examples.py
and view: session_examples/counter.html
1. 2. 3. 4. 5. 6. 7. 8. 9. | {{extend 'layout.html'}} <h1>session counter</h1>
<h2>{{for i in range(counter):}}{{=i}}... {{pass}}</h2>
<a href="{{=URL(r=request)}}">{{=T('click me to count')}}</a>
{{block sidebar}} {{end}}
|
Click to count. The session.counter is persistent for this user and application. Every application within the system has its own separate session management.
Try it here: counter
Template Examples
Example 17
In controller: template_examples.py
1. 2. | def variables(): return dict(a=10, b=20) |
and view: template_examples/variables.html
1. 2. 3. 4. 5. | {{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)).
Try it here: variables
Example 18
In controller: template_examples.py
1. 2. | def test_for(): return dict() |
and view: template_examples/test_for.html
1. 2. 3. 4. 5. 6. 7. 8. | {{extend 'layout.html'}} <h1>For loop</h1>
{{for number in ['one','two','three']:}} <h2>{{=number.capitalize()}}</h2> {{pass}}
|
You can do for and while loops.
Try it here: test_for
Example 19
In controller: template_examples.py
1. 2. | def test_if(): return dict() |
and view: template_examples/test_if.html
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. | {{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.
Try it here: test_if
Example 20
In controller: template_examples.py
1. 2. | def test_try(): return dict() |
and view: template_examples/test_try.html
1. 2. 3. 4. 5. 6. 7. 8. 9. | {{extend 'layout.html'}} <h1>Try... except</h1>
{{try:}} <h2>a={{=1/0}}</h2> {{except:}} infinity</h2> {{pass}}
|
You can do try, except, finally.
Try it here: test_try
Example 21
In controller: template_examples.py
1. 2. | def test_def(): return dict() |
and view: template_examples/test_def.html
1. 2. 3. 4. 5. 6. 7. 8. | {{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.
Try it here: test_def
Example 22
In controller: template_examples.py
1. 2. | def escape(): return dict(message='<h1>text is escaped</h1>') |
and view: template_examples/escape.html
1. 2. 3. 4. 5. 6. | {{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.
Try it here: escape
Example 23
In controller: template_examples.py
1. 2. | def xml(): return dict(message=XML('<h1>text is not escaped</h1>')) |
and view: template_examples/xml.html
1. 2. 3. 4. 5. 6. | {{extend 'layout.html'}} <h1>XML</h1>
<h2>Message is</h2> {{=message}}
|
If you do not want to escape the argument of {{=...}} mark it as XML.
Try it here: xml
Example 24
In controller: template_examples.py
1. 2. | def beautify(): dict(message=BEAUTIFY(dict(a=1,b=[2,3,dict(hello='world')]))) |
and view: template_examples/beautify.html
1. 2. 3. 4. 5. 6. | {{extend 'layout.html'}} <h1>BEAUTIFY</h1>
<h2>Message is</h2> {{=message}}
|
You can use BEAUTIFY to turn lists and dictionaries into organized HTML.
Try it here: beautify
Layout Examples
Example 25
In controller: layout_examples.py
1. 2. 3. 4. 5. 6. | def civilized(): response.menu=[['civilized',True,URL('civilized')], ['slick',False,URL('slick')], ['basic',False,URL('basic')]] response.flash='you clicked on civilized' return dict(message="you clicked on civilized") |
and view: layout_examples/civilized.html
1. 2. 3. 4. | {{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}}.
Try it here: civilized
Example 26
In controller: layout_examples.py
1. 2. 3. 4. 5. 6. | def slick(): response.menu = [['civilized',False,URL('civilized')], ['slick',True,URL('slick')], ['basic',False,URL('basic')]] response.flash = 'you clicked on slick' return dict(message="you clicked on slick") |
and view: layout_examples/slick.html
1. 2. 3. 4. | {{extend 'layout_examples/layout_sleek.html'}} <h2>{{=message}}</h2> {{for i in range(1000):}}bla {{pass}}
|
Same here, but using a different template.
Try it here: slick
Example 27
In controller: layout_examples.py
1. 2. 3. 4. 5. 6. | def basic(): response.menu=[['civilized',False,URL('civilized')], ['slick',False,URL('slick')], ['basic',True,URL('basic')]] response.flash='you clicked on basic' return dict(message="you clicked on basic") |
and view: layout_examples/basic.html
1. 2. 3. 4. | {{extend 'layout.html'}} <h2>{{=message}}</h2> {{for i in range(1000):}}bla {{pass}}
|
'layout.html' is the default template, every application has a copy of it.
Try it here: basic
Example 28
In controller: form_examples.py
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. | 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,session): response.flash="form accepted" elif form.errors: response.flash="form is invalid" else: response.flash="please fill the form" 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(..) tries 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.
Try it here: form
Database Examples
You can find more examples of the web2py Database Abstraction Layer here
Let's create a simple model with users, products (sold by users) and purchases (the database of an animal store). Each user can sell many products (ONE TO MANY). A user can buy many products and each product can have many buyers (MANY TO MANY).
Example 29
in model: db.py
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. | db.define_table( 'person', Field('name'), Field('email'), format = '%(name)s')
# ONE (person) TO MANY (products)
db.define_table( 'product', Field('seller_id',db.person), Field('name'), Field('description', 'text'), Field('picture', 'upload', default=''), format = '%(name)s')
# MANY (persons) TO MANY (purchases)
db.define_table( 'purchase', Field('buyer_id', db.person), Field('product_id', db.product), Field('quantity', 'integer'), format = '%(quantity)s %(product_id)s -> %(buyer_id)s')
purchased = (db.person.id==db.purchase.buyer_id)&(db.product.id==db.purchase.product_id)
db.person.name.requires = IS_NOT_EMPTY() db.person.email.requires = [IS_EMAIL(), IS_NOT_IN_DB(db, 'person.email')] db.product.name.requires = IS_NOT_EMPTY() db.purchase.quantity.requires = IS_INT_IN_RANGE(0, 10) |
Tables are created if they do not exist (try... except).
Here "purchased" is an Query object, "db(purchased)" would be a Set objects. A Set object can be selected, updated, deleted. Sets 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 db.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(db.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 attempt a migration add one last argument to define_table migrate=False.
Example 30
In controller: database_examples.py
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. | response.menu = [['Register Person', False, URL('register_person')], ['Register Product', False, URL('register_product')], ['Buy product', False, URL('buy')]]
def register_person(): # create an insert form from the table form = SQLFORM(db.person).process()
# if form correct perform the insert if form.accepted: response.flash = 'new record inserted'
# and get a list of all persons records = SQLTABLE(db().select(db.person.ALL),headers='fieldname:capitalize')
return dict(form=form, records=records) |
and view: database_examples/register_person.html
1. 2. 3. 4. 5. 6. 7. 8. | {{extend 'layout.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.
Example 31
In controller: database_examples.py
1. 2. 3. 4. 5. 6. 7. 8. | def register_product(): form = SQLFORM(db.product).process() if form.accepted: response.flash = 'new record inserted' records = SQLTABLE(db().select(db.product.ALL), upload = URL('download'), # allows pics preview headers='fieldname:capitalize') return dict(form=form, records=records) |
and view: database_examples/register_product.html
1. 2. 3. 4. 5. 6. 7. 8. | {{extend 'layout.html'}}
<h1>Product registration form</h1> {{=form}} <h2>Current products</h2> {{=records}}
|
Nothing new here.
Example 32
In controller: database_examples.py
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. | def buy(): form = SQLFORM.factory( Field('buyer_id',requires=IS_IN_DB(db,db.person.id,'%(name)s')), Field('product_id',requires=IS_IN_DB(db,db.product.id,'%(name)s')), Field('quantity','integer',requires=IS_INT_IN_RANGE(1,100))).process() if form.accepted: # get previous purchase for same product purchase = db((db.purchase.buyer_id == form.vars.buyer_id)& (db.purchase.product_id==form.vars.product_id)).select().first()
if purchase: # if list contains a record, update that record purchase.update_record( quantity = purchase.quantity+form.vars.quantity) else: # self insert a new record in table db.purchase.insert(buyer_id=form.vars.buyer_id, product_id=form.vars.product_id, quantity=form.vars.quantity) response.flash = 'product purchased!' elif form.errors: response.flash = 'invalid values in form!'
# now get a list of all purchases records = SQLTABLE(db(purchased).select(),headers='fieldname:capitalize') return dict(form=form, records=records) |
and view: database_examples/buy.html
1. 2. 3. 4. 5. 6. 7. 8. | {{extend 'layout.html'}} <h1>Purchase form</h1> {{=form}} [ {{=A('delete purchases',_href=URL('delete_purchased'))}} ] <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.
Example 33
In controller: database_examples.py
This controller allows users to download the uploaded pictures of products.
Remember the upload=URL('download') statement in the register_product 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 useful to allow visitors to link uploaded files.
Example 34
Using a Smartgrid
All of the above database examples can be condensed in one simple command using the SQLFORM.smartgrid:
1. 2. 3. 4. | def manage_transactions(): grid = SQLFORM.smartgrid(db.person,linked_tables=['product','purchase'], user_signature=False) return dict(grid=grid) |
The SQLFORM.smartgrid allows to create/read/delete persons as well as records in the linked tables (product and purchase). It also allows searching with pagination. It can be highly customized. The user_signature=False disables grid access control features which are beyond this simple example.
Cache Examples
Example 35
In controller: cache_examples.py
1. 2. 3. 4. | 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.
Try it here: cache_in_ram
Example 36
In controller: cache_examples.py
1. 2. 3. 4. | 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.
Try it here: cache_on_disk
Example 37
In controller: cache_examples.py
1. 2. 3. 4. 5. | 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.
Try it here: cache_in_ram_and_disk
Example 38
In controller: cache_examples.py
1. 2. 3. 4. 5. | @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(db().select(db.user.ALL)).xml(). You can read below for an even better way to do it.
Try it here: cache_controller_in_ram
Example 39
In controller: cache_examples.py
1. 2. 3. 4. 5. | @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 unpickleable objects.
Try it here: cache_controller_on_disk
Example 40
In controller: cache_examples.py
1. 2. 3. 4. 5. 6. | @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 the best and fastest way of caching!
Try it here: cache_controller_and_view
Example 41
In controller: cache_examples.py
1. 2. 3. 4. 5. 6. | def cache_db_select(): import time db.person.insert(name='somebody',email='gluon@mdp.cti.depaul.edu') records = db().select(db.person.ALL,cache=(cache.ram,5)) if len(records)>20: db(db.person.id>0).delete() return dict(records=records) |
The results of a select are complex unpickleable 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.
Ajax Examples
Example 42
In controller: ajax_examples.py
In view: ajax_examples/index.html
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. | {{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" name = "q" value="web2py"/> <INPUT type="button" value="submit" onclick="ajax('{{=URL('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 id. When called, it sends to the url (via a get) the values of the ids and display the response in the value (of innerHTML) of the target id.
Try it here: index
Example 43
In controller: ajax_examples.py
1. 2. 3. | def flash(): response.flash='this text should appear!' return dict() |
Try it here: flash
Example 44
In controller: ajax_examples.py
1. 2. | def fade(): return dict() |
In view: ajax_examples/fade.html
1. 2. 3. 4. 5. 6. 7. 8. 9. | {{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>
|
Try it here: fade
Excel-like spreadsheet via Ajax
Web2py includes a widget that acts like an Excel-like spreadsheet and can be used to build forms
read more.
Testing Examples
Example 45
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:
1. 2. 3. 4. 5. 6. 7. 8. | 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) |
Streaming Examples
Example 46
It is very easy in web2py to stream large files. Here is an example of a controller that does so:
1. 2. 3. 4. | def streamer(): import os path=os.path.join(request.folder,'private','largefile.mpeg4') return response.stream(open(path,'rb'),chunk_size=4096) |
By default all static files and files stored in 'upload' fields in the database are streamed when larger than 1 MByte.
web2py automatically and transparently handles PARTIAL_CONTENT and RANGE requests.
XML-RPC Examples
Example 47
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 functions remotely via xmlrpc.
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. | from gluon.tools import Service service = Service(globals())
@service.xmlrpc def add(a,b): return a+b
@service.xmlrpc def sub(a,b): return a-b
def call(): return service()
def tester(): import xmlrpclib server=xmlrpclib.ServerProxy('http://hostname:port/app/controller/call/xmlrpc') return str(server.add(3,4)+server.sub(3,4)) |