Components for web2py HTML pages NOT derived from layout.html

I wrote a post to the google web2py-users group on this subject. This WordPress post will be my log of the work i am doing on this subject.

Here is a copy of my original web2py-users group post.

Dear web2py Forum,

Often i find myself making View pages that are NOT derived from layout.html and yet wanting to include some aspects of its capabilities.

I thought the web2py “Component” concept might help to deliver a modular approach.

My initial thoughts would be to pair small CSS files and HTML Component definitions for the following:

  • menu
  • login/register
  • left sidebar
  • right sidebar
  • footer
  • ajax
  • flash response
  • mobile
  • responsive

while maintaining a list of dependencies among them.

Has anyone thought about this granular approach before?

I would not want to use a Wizard for this, because in my experience, the generated code is unfamiliar to me and difficult to customize.

Thanks for a GREAT framework.

Love and peace,

Joe

1. i looked up web2py component

By def it seems that components

communicate with the component controller function via Ajax

Therefore the list of dependencies must contain ajax stuff.

2. add a new component to list:

navbar – is a complex component containing both menu and auth login stuff

 

#components, #css, #gui, #html-helpers, #idioms, #web2py

Web2py URL Mapping

URL Mapping from the Book Chapter 4 – Dispatching. These are the basics there’s more detail in the book.

The URL    http://site.com/a/c/f    maps to:

The function f() in controller “c.py” in application “a“.

  • If f is not present, web2py defaults to the index controller function.
  • If c is not present, web2py defaults to the default.py controller.
  • If a is not present, web2py defaults to the init application.
  • If there is no init application, web2py tries to run the welcome application.
  • The extension .html is optional.
  • The extension .html is assumed as default.
  • The extension determines the extension of the view that renders the output of the controller function f().
  • The extension allows the same content to be served in multiple formats (html, xml, json, rss, etc.).

Web2py maps GET/POST requests of the form:

http://site.com/a/c/f.html/x/y/z?p=1&q=2

As before, to function f in controller “c.py” in application a

and it stores the URL parameters in the requestvariable as follows:

    request.args = ['x', 'y', 'z']
    request.vars = {'p':1, 'q':2}
    request.application = 'a'
    request.controller = 'c'
    request.function = 'f'

    request.url = url of request
    request.ajax = False  #by default
    if request.ajax == True and wasInitiatedByAWeb2pyComponent:
        request.cid = componentName

#python, #web2py

Web2py DAL Quick Reference

Background
The following was referred to here in a post by Iceberg on May 4, 2009 at 9:14 AM on the web2py-users google group. It is a merged version of the old and new DAL info.

I couldn’t find it published anywhere so I am publishing it here so I can refer to it. Some of it is here.

web2pyTM Database Abstraction Layer (DAL)

Quick Reference

define_table, insert, count, delete, update

db = SQLDB('postgres://user:password@hostname/db', pools=10)
db.define_table('person',db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('name','string'))
id= db.person.insert(name='max')
query=(db.person.id==id)
db(query).count()
db(query).delete()
db(query).update(name='Max')
rows = db(query).select(orderby=db.person.name)
for row in rows: print row.name

Examples of uri strings for SQLDB

sqlite://test.db
mysql://user:password@localhost/database
postgres://user:password@localhost/database
mssql://user:password@host/database
firebird://user:password@server:3050/database
oracle://user:password@database

Valid field Types

db.Field(name, 'string')
db.Field(name, 'text')
db.Field(name, 'password')
db.Field(name, 'blob')
db.Field(name, 'upload')
db.Field(name, 'boolean')
db.Field(name, 'integer')
db.Field(name, 'double')
db.Field(name, 'time')
db.Field(name, 'date')
db.Field(name, 'datetime')
db.Field(name, db.referenced_table) # reference field

Valid Field Attributes

length (only for string type, defaults to 32)
default (defaults to None)
required (defaults to False)
notnull (defaults to False)
requires (<a href="http://127.0.0.1:8000/examples/default/api">validator or list of validators</a>, for forms)
comment (for forms)
widget (for forms)
represent (for forms)
readable (for forms)
writable (for forms)
update (default value if the record is updated)
uploadfield (for upload fields)
authorize (for upload fields, function to be used if data can be downloaded, see authentication)
autodelete (for upload fields, if set to true linked uploaded images are removed upon deletion of the record)
label (for forms)

Migrations
Changing the list of fields or field types in a model, triggers an automatic migration, i.e. web2py generates SQL to alter the table accordingly. If the table does not exist it is created. Migration actions are logged in the file sql.log accessibled via the admin/design interface. Migration can be turned off on a per-table basis by passing migrate=False to define_table.
Select Attributes

rows = db(query).select(*fields, orderby=..., left=..., groupby=..., having=..., limitby=..., cache=...)

DAL Shortcuts

db['person']                   ### db.person
db.person['name']              ### db.person.name
db['person']['name']           ### db.person.name
db.person[0]=dict(name='Max')  ### insert
db.person[id]=dict(name='Max') ### update by db.person.id
print db.person[id]            ### select by db.person.id
del db.person[id]              ### delete by db.person.id</pre>

Truncate and Drop a Table

db.person.truncate()
db.person.drop()

**Reference Fields and Inner Joins*

db.define_table('dog',db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('name'))
db.define_table('friendship', db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('person',db.person), db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('dog',db.dog))
db.friendship.insert(person=id, dog=db.dog.insert(name='Snoopy'))
friends=(db.person.id==db.friendship.person)&amp;(db.dog.id==db.friendship.dog)
rows = db(friends).select(db.person.name, db.dog.name)
for row in rows: print row.person.name, 'is friend of', row.dog.name

Left Outer Joins

query=(db.person.id>0)
friends=(db.person.id==db.friendship.person)&(db.dog.id==db.friendship.dog)
rows = db(query).select(db.person.name, db.dog.name, left=db.dog.on(friends))
for row in rows: print row.person.name, 'is friend of', row.dog.name or 'nobody'

Complex Queries

query = (db.person.id==1)|((db.person.id==2)&(db.person.name=='Max'))
query = (db.person.id==db.friendship.person)&(db.dog.id==db.friendship.dog)
query = db.person.name.lower().like('m%')
query = db.person.id.belongs(('max','Max','MAX'))
query = db.person.birth.year()+1==2008
rows = db(query).select()

Nested Selects

query = db.person.id.belongs(db()._select(db.friendship.person)

Aggregates

rows=db(friends).select(db.person.name,db.dog.id.count(),groupby=db.dog.id) 
''' Notes:
Aggregate functions: 
    db.table.field.count()
    db.table.field.max() 
    db.table.field.min()
    db.table.field. sum()
'''

Aliases

person=db.person
friendship=db.friendship
puppy=db.dog.with_alias('puppy')
query=(puppy.id==friendhip.dog)&(friendship.person==person.id)
rows=db().select(person.name,puppy.name,left=puppy.on(query))

Caching

rows=db().select(db.person.ALL,cache=(cache.ram,3600))
''' Notes:
cache=(model,cache_timeout) where model can be:
    cache.ram, 
    cache.disk, 
    cache.memcache or 
    user defined caching model
cache_timeout is in seconds.
'''

CSV Input/Output

# CVS Input
db.person.import_from_csv_file(open(filename,’rb’))
# CVS Output
str(rows)

HTML Output

print rows.xml()

Set Field Validators

db.person.name.requires=IS_NOT_IN_DB(db,db.person.name)
db.friendship.person.requires=IS_IN_DB(db,db.person.id,'%(name)s')

Generate and Process a Form from a Model

form = SQLFORM(db.friendship)
if form.accepts(request.vars, session): response.flash='record inserted'
elif form.errors: response.flash='form errors'
''' Note:
The form can then be displayed in a view with:
{{=form}}
'''

SQL + HTML Examples

# Given:
rows=db().select(db.users.ALL)
# Then:
SQLFORM (rows) # turns the rows into a CSS friendly table
SQLFORM(db.users)  # makes an input form for users
SQLFORM(db.users,rows[i]) # makes an edit form for the user in rows[i]
SQLFORM(db.users,rows[i],deletable=True) # makes an edit/delete form for the user in row[i]
SQLFORM(....,fields=['name','email']) # allows defining which fields should be displayed in the form. Only those fields will be validated.
SQLFORM(....,labels=['name':'Your Name']) # allows changing the labels of the listed fields.

''' Notes:
SQLFORM
-------
A form=SQLFORM(…) object has one method of practical interest:
    form.accepts(request.vars,session) 
The above method: 
    1. Processes the input variables (in request.vars) in the session
    2. Returns true if the form is valid, false otherwise. 
- Processed variables are in     form.vars
- Errors are in                  form.errors. 
- The form is modified accordingly. 
- If the form is accepted: 
    accept also performs the appropriate insert/update/delete in the database.

Extended Usage
--------------
The DAL API is automatically exposed in web2py 
    models, 
    controllers and 
    views 
but you can access them anywhere with:
'''
from gluon.sql import * 

Doctest Examples
These are old syntax but good examples. They use:

  • SQLDB instead of DAL and
  • SQLField instead of Field

However I believe they are still useful.

>>> db=SQLDB("sqlite://test.db")
>>> #OR db=SQLDB("mysql://username:password@host:port/dbname")
>>> #OR db=SQLDB("postgres://username:password@host:port/dbname")

# syntax: SQLField('fieldname','fieldtype',length=32,
#                   required=False, default=None,
#                   requires=[IS_EMAIL(error_message='invalid email')])

>>> tmp=db.define_table('users',
      # But notice that SQLField(...) is depreciated and you'd better use db.Field(...) instead
      SQLField('stringfield','string',length=32,required=True),
      SQLField('booleanfield','boolean',default=False),
      SQLField('passwordfield','password'),
      SQLField('textfield','text'),
      SQLField('blobfield','blob'),
      SQLField('uploadfield','upload'),
      SQLField('integerfield','integer'),
      SQLField('doublefield','double'),
      SQLField('datefield','date',default=datetime.date.today()),
      SQLField('timefield','time'),
      SQLField('datetimefield','datetime'),
      migrate='test_user.table')

# Insert a field

>>> db.users.insert(stringfield='a',booleanfield=True,
                   passwordfield='p',textfield='x',blobfield='x',
                   uploadfield=None, integerfield=5,doublefield=3.14,
                   datefield=datetime.date(2001,1,1),
                   timefield=datetime.time(12,30,15),
                   datetimefield=datetime.datetime(2002,2,2,12,30,15))
1

# Drop the table

>>> db.users.drop()

# Examples of insert, select, update, delete

>>> tmp=db.define_table('person',
          SQLField('name'),
          SQLField('birth','date'),
          migrate='test_person.table')
>>> person_id=db.person.insert(name="Marco",birth='2005-06-22')
>>> person_id=db.person.insert(name="Massimo",birth='1971-12-21')
>>> len(db().select(db.person.ALL))
2
>>> me=db(db.person.id==person_id).select()[0] # test select
>>> me.name
'Massimo'
>>> db(db.person.name=='Massimo').update(name='massimo') # test update
>>> db(db.person.name=='Marco').delete() # test delete

# Update a single record

>>> me.update_record(name="Max")
>>> me.name
'Max'

# Examples of complex search conditions

>>> len(db((db.person.name=='Max')&(db.person.birth>> len(db((db.person.name=='Max')|(db.person.birth>> me=db(db.person.id==person_id).select(db.person.name)[0]
>>> me.name
'Max'

# Examples of search conditions using extract from date/datetime/time

>>> len(db(db.person.birth.month()==12).select())
1
>>> len(db(db.person.birth.year()>1900).select())
1

# Example of usage of NULL

>>> len(db(db.person.birth==None).select()) ### test NULL
0
>>> len(db(db.person.birth!=None).select()) ### test NULL
1

# Examples of search conditions using lower, upper, and like

>>> len(db(db.person.name.upper()=='MAX').select())
1
>>> len(db(db.person.name.like('%ax')).select())
1
>>> len(db(db.person.name.upper().like('%AX')).select())
1
>>> len(db(~db.person.name.upper().like('%AX')).select())
0

# orderby, groupby and limitby

>>> people=db().select(db.person.name,orderby=db.person.name)
>>> order=db.person.name|~db.person.birth
>>> people=db().select(db.person.name,orderby=order)
>>> people=db().select(db.person.name,orderby=order,groupby=db.person.name)
>>> people=db().select(db.person.name,orderby=order,limitby=(0,100))

# Example of one 2 many relation

>>> tmp=db.define_table('dog',
          SQLField('name'),
          SQLField('birth','date'),
          SQLField('owner',db.person),
          migrate='test_dog.table')
>>> db.dog.insert(name='Snoopy',birth=None,owner=person_id)
1

# A simple JOIN

>>> len(db(db.dog.owner==db.person.id).select())
1

# Drop tables

>>> db.dog.drop()
>>> db.person.drop()

# Example of many 2 many relation and SQLSet

>>> tmp=db.define_table('author',SQLField('name'),
                        migrate='test_author.table')
>>> tmp=db.define_table('paper',SQLField('title'),
                        migrate='test_paper.table')
>>> tmp=db.define_table('authorship',
        SQLField('author_id',db.author),
        SQLField('paper_id',db.paper),
        migrate='test_authorship.table')
>>> aid=db.author.insert(name='Massimo')
>>> pid=db.paper.insert(title='QCD')
>>> tmp=db.authorship.insert(author_id=aid,paper_id=pid)

# Define a SQLSet

>>> authored_papers=db((db.author.id==db.authorship.author_id)&
                       (db.paper.id==db.authorship.paper_id))
>>> rows=authored_papers.select(db.author.name,db.paper.title)
>>> for row in rows: print row.author.name, row.paper.title
Massimo QCD

# Example of search condition using  belongs

>>> set=(1,2,3)
>>> rows=db(db.paper.id.belongs(set)).select(db.paper.ALL)
>>> print rows[0].title
QCD

# Example of search condition using nested select

>>> nested_select=db()._select(db.authorship.paper_id)
>>> rows=db(db.paper.id.belongs(nested_select)).select(db.paper.ALL)
>>> print rows[0].title
QCD

# Output in csv

>>> str(authored_papers.select(db.author.name,db.paper.title))
author.name,paper.title
Massimo,QCD

# Delete all leftover tables

>>> db.authorship.drop()
>>> db.author.drop()
>>> db.paper.drop()

# Commit or rollback your work

>>> db.commit() # or db.rollback()

''' Notes:
migrate can be:
    False (do not create/alter tables), 
    True (create/alter tables) or 
    a filename (create/alter tables and store migration information in the file).

There are little idiosyncrasies in every  backend like the fact that:
    "user" is not a valid field name in PostgreSQL, or 
    that sqlite3 will ignore the type of a field and allow you to put anything in it despite the declared type.
Each database has its own keywords that may conflict with your tablenames.

#dal, #web2py

Making my web2py site use a default_application other than welcome on WebFaction.

Here are the steps on webfaction.

  1. $ cd ~/your_path_to_web2py/web2py
  2. $ cp routes.example.py routes.py #there is no routes.py at first
  3. [edit routes.py as follows
        line was: default_application = ‘init’    # ordinarily set in base routes.py
        line now: default_application = ‘other’   # ordinarily set in base routes.py
  4. $ cd ../apache2/bin
  5. $ ./restart

#app, #default, #web2py

Fixing my "my web2py is unable to create a new application on WebFaction" problem.

Please go here to read a full discussion of the problem. Here is a summary of the steps i took to fix this problem.

  1. Made sure that memory capacity is not a problem.
    1. SSHed into my WebFaction account.
    2. $ ps -u user -o pid,rss,command
      output is: PID, Total memory in bytes, full path of process.
    3. I saw that i was over my memory limit.
    4. Upgraded to get more memory.
    5. Tested to see if that worked. IT DID NOT.
  2. I read this thread.
    1. Made sure that the web2py directory contained a directory named “deposit”.
    2. It did.
    3. Re-tested – NO GOOD.
  3. Opened a WebFaction Ticket.
    1. WebFaction Support suggested that I “…try running the web2py daemon script manually (“python2.5 web2py[.py]”) to initialize those systems”.
    2. Decided i would do that.
  4. Ran web2py from the SSH command line to initialize it.
    1. $ cd ~/path_to_troubled_web2py_instance/web2py/
    2. $ python2.5 web2py.py
      Up came web2py saying:
      ****************
      web2py Enterprise Web Framework
      Created by Massimo Di Pierro, Copyright 2007-2011
      Version 1.92.1 (2011-02-16 15:04:40)
      Database drivers available: SQLite3, pymysql, PostgreSQL
      Starting hardcron…
      choose a password:
      please visit:
       
      http://127.0.0.1:8000/
      use “kill -SIGTERM 6744” to shutdown the web2py server
      ****************
    3. I let it run for a couple of minutes to initialize.
    4. $ kill -SIGTERM 6744  # The number 6744 varies. Use the number mentioned by web2py.
    5. Re-tested by by using IE8 to https login to the associated web2py admin from my windows XP desktop. SUCCESS. I was able to create the “home” app.  


#web2py

Concatenating web2py HTML Helpers

Say you want to produce something like this list of links dynamically, into an HTML page, from a web2py controller:

mobyJoe.me

 
Here is what you would put into your controller to concatenate (append) HTML Helpers:
def index():
    page_info = DIV(
                A(‘NOAA Weather’, _href=’http://mobile.weather.gov/’), BR(),
                A(‘Google Mobile’, _href=’http://m.google.com/’), BR(),
                A(‘Twitter Mobile’, _href=’http://mobile.twitter.com/’)
                )
    return dict(page_info=page_info)
 
Here is what would go into your associated view:
{{extend ‘layout.html’}}
{{=page_info}}
 
Here’s the reason. Lot’s of HTML helpers are derived from the DIV helper. According to the web2py epydocs at line 523, DIV
"Behaves like a dictionary regarding updating of attributes. 
Behaves like a list regarding inserting/appending components."
 

#html-helpers, #web2py