Get web2py Debugging Info

Get Debugging Info

Here’s a quick way to get debugging info (Mobile or NOT).

from gluon.contrib.user_agent_parser import mobilize
import datetime
@mobilize
def index():
""" Here's an easy web2py debugging action [mobile or NOT] """
response.flash = T("Welcome to web2py!")
m = str(datetime.datetime.now()) +"\n"
m += str(request.user_agent()) +"\n"
m += "\n"
m += "Hi. We have skipped a line."
return dict(message=PRE(T(m)))

Output:
2015-12-03 19:47:23.762934
<Storage {'os': <Storage {'version': 'NT 10.0', 'name': 'Windows'}>, 'is_tablet': False, 'is_mobile': False, 'browser': <Storage {'version': '46.0.2490.86', 'name': 'Chrome'}>}>

Hi. Is next line.

#debug, #web2py

Easy to Understand web2py Grid Custom Search

web2py Grid Custom Search WITHOUT specifying a custom search_widget

The custom_search.html view contains the EASIER TO UNDERSTAND customization code. Here is the technique.

  1. Make the SQLFORM.grid’s Standard Search Input hidden.
  2. Define Custom Search Input elements with onchange events that send their values to the to the hidden Standard Search Input.
  3. Insert the Custom Search Input elements after the Standard Search Input (“#w2p_keywords”) using jQuery .insertAfter().
    • This prevents them from showing up on Edit or View pages.
    • Insert them in reverse order of them appearing on the page.

You can find an older version of this on web2pyslices.com

Here is the Controller code. Note the absence of a custom search_widget argument in the grid function call.

# in default.py Controller
def custom_search():
    '''
    Implements SQLFORM.grid custom search 
        WITHOUT specifying a custom search_widget,
            and so needing to read & understand the clever web2py implementation source code.
    The custom_search.html view contains the EASIER TO UNDERSTAND customization code.
    The technique:
        1. Make the grid's Standard Search Input hidden.
        2. Define Custom Search Input elements 
            with onchange events that 
                send their values to the to the hidden Standard Search Input.
    '''
    query=((db.contact.id > 0))
    fields = (db.contact.id, 
        db.contact.l_name, 
        db.contact.f_name, 
        db.contact.prime_phone,
        db.contact.date_modified,
        )

    headers = {'contact.id':   'ID',
           'contact.l_name': 'Last Name',
           'contact.f_name': 'First Name',
           'contact.prime_phone': 'Primary Phone',
           'contact.date_modified': 'Info Last Updated',
           }    
    init_sort_order=[db.contact.l_name]   

    grid = SQLFORM.grid(query=query, 
        fields=fields, 
        headers=headers, 
        orderby=init_sort_order,
        searchable=True,  
        user_signature=False, 
        create=True, deletable=False, editable=True, maxtextlength=100, paginate=25)

    return dict(grid=grid)    

Here is the View code.

<!-- In custom_search.html view -->
{{extend 'layout.html'}}
{{block head}}
{{super}}
<script>

function phoneSrch(){
    var srch ='contact.prime_phone contains '+'"'+jQuery('#joephone').val()+'"';
    $("#w2p_keywords").val(srch);
}
function lnameSrch(){
    var srch ='contact.l_name starts with '+'"'+jQuery('#joelname').val()+'"';
    $("#w2p_keywords").val(srch);
}

$(document).ready(function(){
  // Make the Grid Standard Search Input hidden  
  $("#w2p_keywords").prop("type", "hidden");   

  // Insert the Custom Search Input elements after 
  //     the Standard Search Input ("#w2p_keywords")
  //     using jQuery .insertAfter().
  //     This prevents them from showing up on Edit or View pages.
  //     Insert them in reverse order of them appearing on the page.
  var input2Str  = '<div class="joeinputclass" style="padding-bottom:10px;" >';
  input2Str += '<span class="joelabelclass" >Primary Phone contains: ';
  input2Str += '</span><input name="joephone" id="joephone" type="text" ';
  input2Str += 'onchange="phoneSrch()" style="width:150px;" ><br/></div>';
  $(input2Str).insertAfter("#w2p_keywords");
  var input1Str  = '<div class="joeinputclass" style="padding-bottom:10px;">';
  input1Str += '<span class="joelabelclass" style="padding-right:18px;" >';
  input1Str += 'Last Name starts with: </span><input name="joelname" ';
  input1Str += 'id="joelname" type="text"  onchange="lnameSrch()" ';
  input1Str += 'style="width:150px;" ></div>';
  $(input1Str).insertAfter("#w2p_keywords");
});

</script>
{{end}}
<h2>Contacts</h2>
<div id="theweb2pygrid">
{{=grid}}
</div>

#custom-search, #grid, #web2py

Good contact Table

##Contact Table Def
Here’s a good contact table def i want to remember.

  • Automatic change for Info Last Updated datetime field.
  • Uses represent
db.define_table("contact",
    Field("f_name", "string",  requires=IS_NOT_EMPTY() ),
    Field("l_name", "string", requires=IS_NOT_EMPTY() ),
    Field("m_initial", "string", default=None),
    Field('prime_phone', requires=IS_NOT_EMPTY(), comment='phone eg: (650) 555-1212'),
    Field('other_phone',   comment='phone eg: (650) 555-1212'),
    Field('email', requires=IS_EMPTY_OR(IS_EMAIL()) ),    
    #Field('ca_address', 'reference ca_address', writable=False, readable=False),
    Field("address_line1", "string", requires=IS_NOT_EMPTY() ),
    Field("address_line2", "string"),
    Field("city", "string", requires=IS_NOT_EMPTY() ),
    Field("state", "string", length=2, default='CA', writable=False),
    Field("zip", "string", length=5, default=None),
    Field("date_modified", "datetime", default=datetime.datetime.now(),
        update=request.now,   # working - needed below line
        writable=False, readable=True,
    # http://strftime.org/   AND http://stackoverflow.com/questions/9678172/modify-column-output-for-sqlform-grid-in-web2py
        represent=lambda x, row: x.strftime("%c"),
        comment='Last change date of contact info.'),

      ) 

#dal, #python, #web2py

Debug web2py Actions with iPython

My Notes re debugging web2py Actions with iPython

My system

  • Using web2py 2.9.5-stable+timestamp.2014.03.16.02.35.39 (Running on Rocket 1.2.6, Python 2.7.5)
  • On Windows XP in C:\web2py

iPython log

Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]
Type "copyright", "credits" or "license" for more information.

IPython 0.13.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: cd C:\web2py
C:\web2py

In [2]: from gluon import shell

In [3]: shell.run('fish/default/view_or_add_client_trips/?client_id=2', plain=True, import_models=True)
{'grid': <gluon.html.DIV object at 0x0289C930>}

In [4]:

#debug, #ipython, #web2py

Updated web2py DAL Basic Examples

DAL Updated Examples

Content

Table: Define [a Table], Insert, Select, Update, Count, Delete, Drop [a Table]

Also See in the web2py Book:

# make this function now for later result printing
def p(rows):
    for r in rows:
        print r.name, r.addr, r.city

# Content: Define, Insert, Select, Update, Count, Delete, Drop

# Define [a Table]
db.define_table('person', Field('name'), Field('addr'), Field('city') )   # person has 4 fields person.id + person.name .addr .city

# Insert
db.person.insert( **{'name':'joe', 'addr':'5 Blue St', 'city':'waco'} )
db.person.insert( **{'name':'jim', 'addr':'6 Red St', 'city':'waco'} )
db.person.insert( **{'name':'john', 'addr':'7 Green St', 'city':'waco'} )
db.person.insert( **{'name':'jack', 'addr':'7 Green St', 'city':'houston'} )
db.person.insert( **{'name':'jackie', 'addr':'7 Green St', 'city':'houston'} )
db.person.insert( **{'name':'jill', 'addr':'7 Green St', 'city':'austin'} )
db.person.insert( **{'name':'jane', 'addr':'9 Brown St', 'city':'austin'} )


# Select        
# select all rows in person
rows = db(db.person.id > 0).select()
In [55]: p(rows)
joe 5 Blue St waco
jim 6 Red St waco
john 7 Green St waco
jack 7 Green St houston
jackie 7 Green St houston
jill 7 Green St austin
jane 9 Brown St austin

# select rows in city is waco
rows = db(db.person.city == 'waco').select()
In [61]: p(rows)
joe 5 Blue St waco
jim 6 Red St waco
john 7 Green St waco

# select rows in city is 'waco' AND addr is '7 Green St'
rows = db( (db.person.city == 'waco') & (db.person.addr == '7 Green St') ).select()
In [73]: p(rows)
john 7 Green St waco

# select rows in city is 'houston' OR name is 'joe'
rows = db( (db.person.city == 'houston') | (db.person.name == 'joe') ).select()
In [83]: p(rows)
joe 5 Blue St waco
jack 7 Green St houston
jackie 7 Green St houston

# Update  ->   get an id;   do the update
# update 1    update spelling jackie's name to 'jaquay'
id = db( (db.person.name=='jackie') & (db.person.addr=='7 Green St') & (db.person.city=='houston') ).select().first().id 
rtn = db.person[id] = dict(name='jaquay', addr='7 Green St', city='houston')

# update 1 result
In [87]: rtn
Out[87]: {'addr': '7 Green St', 'city': 'houston', 'name': 'jaquay'}
In [88]: rows = db(db.person.id > 0).select()
In [89]: p(rows)
joe 5 Blue St waco
jim 6 Red St waco
john 7 Green St waco
jack 7 Green St houston
jaquay 7 Green St houston
jill 7 Green St austin
jane 9 Brown St austin

# update 2    all '7 Green St's in 'houston' to '7 Yellow St' in 'houston'
rows = db( (db.person.addr=='7 Green St') & (db.person.city=='houston') ).select()
for row in rows:
    rtn = db.person[row.id] = dict(addr='7 Yellow St', city='houston')
    print rtn
# update 2 result
rows = db(db.person.id > 0).select()
In [94]: p(rows)
joe 5 Blue St waco
jim 6 Red St waco
john 7 Green St waco
jack 7 Yellow St houston
jaquay 7 Yellow St houston
jill 7 Green St austin
jane 9 Brown St austin


# Count

# count 1    items in city is 'houston' OR name is 'joe'
In [97]: print db( (db.person.city == 'houston') | (db.person.name == 'joe') ).count()
3
# notice relationship to .select()
In [98]: print db( (db.person.city == 'houston') | (db.person.name == 'joe') ).select()
person.id,person.name,person.addr,person.city
1,joe,5 Blue St,waco
4,jack,7 Yellow St,houston
5,jaquay,7 Yellow St,houston

# count 2    items where name startswith 'ja'
In [99]: print db( (db.person.name.startswith('ja'))  ).count()
3
In [100]: print db( (db.person.name.startswith('ja'))  ).select()
person.id,person.name,person.addr,person.city
4,jack,7 Yellow St,houston
5,jaquay,7 Yellow St,houston
7,jane,9 Brown St,austin

# Delete   delete all items where name startswith('jo')  notice similarity to select()

rtn = db( (db.person.name.startswith('jo'))  ).delete()
In [105]: print rtn
2
rows = db(db.person.id > 0).select()
In [107]: p(rows)
jim 6 Red St waco
jack 7 Yellow St houston
jaquay 7 Yellow St houston
jill 7 Green St austin
jane 9 Brown St austin


# Drop [a Table]   drop the table from the db
db.person.drop()


see_also = '''
[Other operators](http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Other-operators)
[Combining rows](http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Combining-rows)
[list:<type> and contains](http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#list--type--and-contains)
'''

#dal, #examples, #python, #web2py

web2py Field Exegesis

web2py Field Exegesis

Joe Dorocak aka Joe Codeswell has written working Python code examples exploring “fully qualified” web2py Field constructors.

I wrote this to turn the web2py book chapter on the Field constructor into working, “fully qualified” examples, so I could understand the meaning and explore the features. There are 2 “fully qualified” examples in mytable for now:

  • fulldefaultstrfield – is my cut at fully qualifying the default settings for the Field constructor named parameters.
  • fullplay1strfield – is my 1st cut at playing around with the values of the named parameters in the Field constructor.

The results can be seen by implementing this locally and navigating to:

  • inserting new records at http://127.0.0.1:8000/fields/appadmin/select/db
  • seeing the table grid at http://127.0.0.1:8000/fields/default/gridmytable

Content

  • In applications/fields/controllers/default.py
  • In applications/fields/models/db.py

In applications/fields/controllers/default.py

# -*- coding: utf-8 -*-
# In applications/fields/controllers/default.py

def gridmytable():
    grid = SQLFORM.grid(db.mytable)
    return locals()

In applications/fields/models/db.py

# -*- coding: utf-8 -*-
# In applications/fields/models/db.py

# web2py Field Exegesis
# Joe Dorocak aka Joe Codeswell has written working Python code examples exploring "fully qualified" web2py Field constructors. 
# I wrote this to turn the web2py book chapter on the Field constructor into working, "fully qualified" examples, so i could understand the meaning and explore the features. There are 2 "fully qualified" examples in mytable for now.
#  - fulldefaultstrfield - is my cut at fullyqualifying the default settings for the Field constructor named parameters.
#  - fullplay1strfield   - is my 1st cut at playing around with the values of the named parameters in the Field constructor.
# The results can be seen by implementing this locally and navigating to
#   -  inserting new records at   http://127.0.0.1:8000/fields/appadmin/select/db 
#   -  seeing the table grid at   http://127.0.0.1:8000/fields/default/gridmytable


# BOILERPLATE
if not request.env.web2py_runtime_gae:
    db = DAL('sqlite://storage.sqlite',pool_size=1,check_reserved=['all'])
else:
    db = DAL('google:datastore')
    session.connect(request, response, db=db)

response.generic_patterns = ['*'] if request.is_local else []

from gluon.tools import Auth, Crud, Service, PluginManager, prettydate
auth = Auth(db)
crud, service, plugins = Crud(db), Service(), PluginManager()

## create all tables needed by auth if not custom tables
auth.define_tables(username=False, signature=False)
# END BOILERPLATE



# START HERE
db.define_table('mytable',

        Field('myfield','string'),

        # see http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Field-constructor
        # a fully defined field example that will HOPEFULLY work
        # Not all of the parameters are relevant for every field type.
        Field(
         'fulldefaultstrfield',        # Field name positional arg
          type='string',               # Field type see field_types below

          # Sets the maximum length of a "string", "password" or "upload" field.
          #     See "2.6.1 - 2.6.4" in  http://www.web2py.com/init/default/changelog
          #         "MySQL users: The length of string fields changed from 255 to 512 bytes. 
          length=512,       

          default='',                  # Default value used to pre-populate forms, can instead be a [lambda] function 
          required=False,              # True => A val for field MUST be specified at record insertion.
          requires=IS_LENGTH(512),     # Is a validator or a list of validators. see book /forms-and-validators#Validators. see field_types below    # joeNote: bad_value == '<DEFAULT>'
          ondelete='CASCADE',          # Defines actions on record delete. CASCADE => delete all referring records
          notnull=False,               # True => fulldeffield can't be Null at record insertion.
          unique=False,                # True => must be unique in the table [not quite the same as IS_NOT_IN_DB]
          uploadfield=True,            # Applies only to fields of type "upload" [Even though True is default and works for type=='string']. See upload_field_type_notes below

          # Must be one of the available widget objects, including custom widgets. 
          # Each field type has a default widget. See widgetList below   
          # This is default for fulldeffield.type=='string'          
          widget=SQLFORM.widgets.string.widget, 

          label=None,                  # Is a string (or a helper or something that can be serialized to a string) that contains the label to be used for this field in auto-generated forms.
                                       #     JoeNote: There's a diff between label=None => default label & label='' => NO LABEL SHOWN!
          comment='',                  # Is a string (or a helper or something that can be serialized to a string) that contains a comment associated with this field, and will be displayed to the right of the input field in the autogenerated forms. 
                                       #      JoeNote: No diff between comment='' & comment=None
          # If a field is neither readable nor writable, it will not be displayed in create and update forms.
          writable=True,               # Declares whether a field is writable in forms.
          readable=True,               # Declares whether a field is readable in forms.

          update=None,                 # Contains the default value for this field when the record is updated.

          authorize=None,              # For "upload" fields only, can be used to require access control on the corresponding field. See Authentication and also Authorization http://web2py.com/books/default/chapter/29/09/access-control.
          autodelete=False,            # For "upload" fields only, determines if the corresponding uploaded file should be deleted when the record referencing the file is deleted. 

          represent=None,              # Can be None or can point to a function that takes a field value and returns an alternate representation for the field value. See representation_examples below.         
          compute=None,                # See compute_notes below.

          uploadfolder=None,           # See upload_field_type_notes below
          uploadseparate=None,         # See upload_field_type_notes below
          uploadfs=None                # See upload_field_type_notes below
        ), 

        # joe says do a diff on following & above to hilight what changed
        #    i think i changed: fieldname, length, requires, label, comment, represent
        # need to provide insert & rendering actions to see diffs in result

        # see http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Field-constructor
        # a fully defined field example that will HOPEFULLY work
        # Not all of the parameters are relevant for every field type.
        Field(
         'fullplay1strfield',          # Field name positional arg
          type='string',               # Field type see field_types below

          # Sets the maximum length of a "string", "password" or "upload" field.
          #     See "2.6.1 - 2.6.4" in  http://www.web2py.com/init/default/changelog
          #         "MySQL users: The length of string fields changed from 255 to 512 bytes. 
          length=4096,       

          default='',                  # Default value used to pre-populate forms, can instead be a [lambda] function 
          required=False,              # True => A val for field MUST be specified at record insertion.
          requires=IS_LENGTH(4096),    # Is a validator or a list of validators. see book /forms-and-validators#Validators. see field_types below    # joeNote: bad_value == '<DEFAULT>'
          ondelete='CASCADE',          # Defines actions on record delete. CASCADE => delete all referring records
          notnull=False,               # True => fulldeffield can't be Null at record insertion.
          unique=False,                # True => must be unique in the table [not quite the same as IS_NOT_IN_DB]
          uploadfield=True,            # Applies only to fields of type "upload" [Even though True is default and works for type=='string']. See upload_field_type_notes below

          # Must be one of the available widget objects, including custom widgets. 
          # Each field type has a default widget. See widgetList below   
          # This is default for fulldeffield.type=='string'          
          widget=SQLFORM.widgets.string.widget, 

          label='lblPlay1strfield',    # Is a string (or a helper or something that can be serialized to a string) that contains the label to be used for this field in auto-generated forms.
                                       #     JoeNote: There's a diff between label=None => default label & label='' => NO LABEL SHOWN!
          comment='cmtPlay1strfield',  # Is a string (or a helper or something that can be serialized to a string) that contains a comment associated with this field, and will be displayed to the right of the input field in the autogenerated forms. 
                                       #      JoeNote: No diff between comment='' & comment=None
          # If a field is neither readable nor writable, it will not be displayed in create and update forms.
          writable=True,               # Declares whether a field is writable in forms.
          readable=True,               # Declares whether a field is readable in forms.

          update=None,                 # Contains the default value for this field when the record is updated.

          authorize=None,              # For "upload" fields only, can be used to require access control on the corresponding field. See Authentication and also Authorization http://web2py.com/books/default/chapter/29/09/access-control.
          autodelete=False,            # For "upload" fields only, determines if the corresponding uploaded file should be deleted when the record referencing the file is deleted. 

          # Can be None or can point to a function that takes a field value and returns an alternate representation for the field value. See representation_examples below.         
          # represent=lambda fullplay1strfield,row: fullplay1strfield.capitalize(),              
          #     result ticket: File "C:/web2py/applications/fields/models/db.py", line 194, in <lambda>
          #         represent=lambda fullplay1strfield,row: fullplay1strfield.capitalize(),
          #         AttributeError: 'NoneType' object has no attribute 'capitalize'
          #    it was None

          represent=lambda fullplay1strfield,row: fullplay1strfield.capitalize(),
          compute=None,                # See compute_notes below.

          uploadfolder=None,           # See upload_field_type_notes below
          uploadseparate=None,         # See upload_field_type_notes below
          uploadfs=None                # See upload_field_type_notes below
        ), 



    )



# see http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Field-types
field_types = '''
field_type              default_field_validators
string                  IS_LENGTH(length) default length is 512
text                    IS_LENGTH(65536)
blob                    None
boolean                 None
integer                 IS_INT_IN_RANGE(-1e100, 1e100)
double                  IS_FLOAT_IN_RANGE(-1e100, 1e100)
decimal(n,m)            IS_DECIMAL_IN_RANGE(-1e100, 1e100)
date                    IS_DATE()
time                    IS_TIME()
datetime                IS_DATETIME()
password                None
upload                  None
reference <table>     IS_IN_DB(db,table.field,format)
list:string             None
list:integer            None
list:reference <table>    IS_IN_DB(db,table.field,format,multiple=True)
json                    IS_JSON()
bigint                  None
big-id                  None
big-reference           None
'''

upload_field_type_notes = '''
    uploadfield:
        applies only to fields of type "upload". A field of type "upload" stores the name of a file saved somewhere else, by default on the filesystem under the application "uploads/" folder. If uploadfield is set to True, then the file is stored in a blob field within the same table and the value of uploadfield is the name of the blob field. This will be discussed in more detail later in the context of SQLFORM.
    uploadfolder:
        defaults to the application's "uploads/" folder. If set to a different path, files will uploaded to a different folder. For example,
            Field(...,uploadfolder=os.path.join(request.folder,'static/temp'))
        will upload files to the "web2py/applications/myapp/static/temp" folder.
    uploadseparate:
        if set to True will upload files under different subfolders of the uploadfolder folder. This is optimized to avoid too many files under the same folder/subfolder. ATTENTION: You cannot change the value of uploadseparate from True to False without breaking links to existing uploads. web2py either uses the separate subfolders or it does not. Changing the behavior after files have been uploaded will prevent web2py from being able to retrieve those files. If this happens it is possible to move files and fix the problem but this is not described here.
    uploadfs:
        allows you specify a different file system where to upload files, including an Amazon S3 storage or a remote SFTP storage. This option requires PyFileSystem installed. uploadfs must point to PyFileSystem. uploadfs
'''

widgetList = [
# See http://web2py.com/books/default/chapter/29/07/forms-and-validators?search=widgets#Widgets
# Here is a list of available web2py widgets:
    SQLFORM.widgets.string.widget,         # default for corresonding type
    SQLFORM.widgets.text.widget,           # default for corresonding type
    SQLFORM.widgets.password.widget,       # default for corresonding type
    SQLFORM.widgets.integer.widget,        # default for corresonding type
    SQLFORM.widgets.double.widget,         # default for corresonding type
    SQLFORM.widgets.time.widget,           # default for corresonding type
    SQLFORM.widgets.date.widget,           # default for corresonding type
    SQLFORM.widgets.datetime.widget,       # default for corresonding type
    SQLFORM.widgets.upload.widget,         # default for corresonding type
    SQLFORM.widgets.boolean.widget,        # default for corresonding type

    # The next 2 widgets are used when a field's requires is IS_IN_SET or IS_IN_DB
    # The "options" widget is used with multiple=False (default behavior), when a field's requires is IS_IN_SET or IS_IN_DB.
    SQLFORM.widgets.options.widget,
    # The "multiple" widget is used with multiple=True, when a field's requires is IS_IN_SET or IS_IN_DB. 
    SQLFORM.widgets.multiple.widget,

    # The "radio" and "checkboxes" widgets are never used by default, but can be set manually. 
    SQLFORM.widgets.radio.widget,    
    SQLFORM.widgets.checkboxes.widget,

    # See http://web2py.com/books/default/chapter/29/07/forms-and-validators?search=widgets#Autocomplete-widget
    SQLFORM.widgets.autocomplete,
    ]

representation_examples = '''
db.mytable.name.represent = lambda name,row: name.capitalize()
db.mytable.other_id.represent = lambda id,row: row.myfield
db.mytable.some_uploadfield.represent = lambda value,row:     A('get it', _href=URL('download', args=value))

"blob" fields are also special. 
By default, binary data is encoded in base64 before being stored into the actual database field, and it is decoded when extracted. 
This has the negative effect of using 25% more storage space than necessary in blob fields, but has two advantages. 
On average it reduces the amount of data communicated between web2py and the database server, and it makes the communication independent of back-end-specific escaping conventions.
'''    

compute_notes = '''
from http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Computed-fields
Computed fields

DAL fields may have a compute attribute. This must be a function (or lambda) that takes a Row object and returns a value for the field. When a new record is modified, including both insertions and updates, if a value for the field is not provided, web2py tries to compute from the other field values using the compute function. Here is an example:

>>> db.define_table('item',
        Field('unit_price','double'),
        Field('quantity','integer'),
        Field('total_price',
            compute=lambda r: r['unit_price']*r['quantity']))
>>> r = db.item.insert(unit_price=1.99, quantity=5)
>>> print r.total_price
9.95

Notice that the computed value is stored in the db and it is not computed on retrieval, as in the case of virtual fields, described later. Two typical applications of computed fields are:

    in wiki applications, to store the processed input wiki text as HTML, to avoid re-processing on every request
    for searching, to compute normalized values for a field, to be used for searching.

Computed fields are evaluated in the order in which they are defined in the table definition. A computed field can refer to previously defined computed fields (new after v 2.5.1)
Virtual fields

Virtual fields are also computed fields ...
    see: http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Virtual-fields
'''

grid_notes = '''

SQLFORM.grid signature
    http://web2py.com/books/default/chapter/29/07/forms-and-validators#SQLFORM-grid-signature

The complete signature for the grid is the following:

SQLFORM.grid(
    query,
    fields=None,
    field_id=None,
    left=None,
    headers={},
    orderby=None,
    groupby=None,
    searchable=True,
    sortable=True,
    paginate=20,
    deletable=True,
    editable=True,
    details=True,
    selectable=None,
    create=True,
    csv=True,
    links=None,
    links_in_grid=True,
    upload='<default>',
    args=[],
    user_signature=True,
    maxtextlengths={},
    maxtextlength=20,
    onvalidation=None,
    oncreate=None,
    onupdate=None,
    ondelete=None,
    sorter_icons=(XML('↑'), XML('↓')),
    ui = 'web2py',
    showbuttontext=True,
    _class="web2py_grid",
    formname='web2py_grid',
    search_widget='default',
    ignore_rw = False,
    formstyle = 'table3cols',
    exportclasses = None,
    formargs={},
    createargs={},
    editargs={},
    viewargs={},
    buttons_placement = 'right',
    links_placement = 'right'
    )

...
JoeNite: below => see SQLFORM
    http://web2py.com/books/default/chapter/29/07/forms-and-validators#SQLFORM
        http://web2py.com/books/default/chapter/29/07/forms-and-validators#SQLFORM-and-insert-update-delete
            google: SQLFORM for select
                https://groups.google.com/forum/#!topic/web2py/VKvw3Mn-mNQ


Because the edit/create form is an SQLFORM which extends FORM, these callbacks are essentially used in the same way as documented in the sections for FORM and SQLFORM.

Here is skeleton code:

def myonvalidation(form):
    print "In onvalidation callback"
    print form.vars
    form.errors= True  #this prevents the submission from completing

    #...or to add messages to specific elements on the form
    form.errors.first_name = "Do not name your child after prominent deities" 
    form.errors.last_name = "Last names must start with a letter"
    response.flash = "I don't like your submission" 

def myoncreate(form):
    print 'create!'
    print form.vars

def myonupdate(form):
    print 'update!'
    print form.vars

def myondelete(table, id):
    print 'delete!'
    print table, id

onupdate and oncreate are the same callbacks available to SQLFORM.process()    
'''    

#examples, #python, #web2py

web2py TO WordPress API with OAuth2

web2py TO WordPress API with OAuth2

This is an example of a web2py app using the WordPress.com REST API with OAuth2.

You need to go to developer.WordPress.com to setup your app.

N.B. I tested this on my website, however, if I messed up with writing this article, please let me know in the comments.

I found some hints on github Automattic

Love and peace,

Joe

controllers

default.py

import datetime, random, string
import urllib, urllib2
import requests

# TODO: Replace these globals with your own.
HOST      = 'yoursite.com'        # REGISTER with developer.wordpress.com
APPNAME   = 'yourappname'         # REGISTER with developer.wordpress.com
CLIENT_SECRET = 'BIG.LONG.STRING' # GET FROM developer.wordpress.com
CLIENT_ID = '12345'               # GET FROM developer.wordpress.com


def authorize_blog():
    """
    Initiates retrieval of OAuth2 credentials which 
        allows your user to aurhorize her blog for use by your app.   
    Generates an HTML page containing a WordPress Authorize button which 
        holds a redirect_uri callback to your app.
    When your user presses this button, she is sent to WordPress.com, where,
        IF she AUTHORIZES one of her blogs:
            WordPress.com will send OAuth2 credentials to YOUR callback URL.   
    see: http://developer.wordpress.com/docs/oauth2/            
    """

    # see [OAuth2 state Article on stackoverflow] (http://stackoverflow.com/questions/11071482/oauth2-0-server-stack-how-to-use-state-to-prevent-csrf-for-draft2-0-v20)
    state = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(30))
    state = str(datetime.datetime.now()).replace(':', '.').replace(' ', '') + '_' + state
    session.state = state


    params = {
        "response_type": "code",
        "client_id": CLIENT_ID,
        "redirect_uri": URL(f='connected', scheme='http', host=HOST, url_encode=False),
        "state": state,
    }

    authorize_url_0 = URL(a='oauth2', c='authorize', f='', vars=params, scheme='https', host='public-api.wordpress.com', url_encode=False)

    # The web2py URL function inserts an (in this case) UNNEEDED reference to the current web2py app.
    #     This string function gets rid of that reference. 
    authorize_url =  authorize_url_0.replace(APPNAME+'/', '',1)

    thehtml = XML(
        '<html><body><h2>Connect FROM YourWebsite.com TO WordPress.com</h2><a href="' +
        authorize_url +
        '"><img src="//s0.wp.com/i/wpcc-button.png" width="231" /></a></body></html>'
    ).xml()

    return thehtml

def connected():
    """
    1. Receive request.vars.code == a "time-limited code that your application can exchange for a full authorization token."
    see: http://developer.wordpress.com/docs/oauth2/   
        This is a time-limited code that your application can exchange for a full authorization token. 
        To do this you will need to pass the code to the token endpoint by making a POST request to the token endpoint: 
            You are required to pass 
                client_id, 
                client_secret, and 
                redirect_uri for web applications. 
                    These parameters have to match the details for your application, and 
                        the redirect_uri must match the redirect_uri used during the Authorize step (above). 
                grant_type has to be set to "authorization_code".   
    2. Convert time-limited code into  OAuth2 full authorization credentials.
        [NOT SHOWN: how to store credentials in db]    
    3. For demo purposes, list the blog with the credentials.    
    """

    # 1. Receive request.vars.code
    code = request.vars.code   # returned from WordPress.com
    if not code:
        redirect(URL('index'))

    # see [OAuth2 state Article on stackoverflow] (http://stackoverflow.com/questions/11071482/oauth2-0-server-stack-how-to-use-state-to-prevent-csrf-for-draft2-0-v20)    
    state = request.vars.state
    if not state:
        return dict(message='Warning! State variable missing after authorization.')

    if (not session.state):
        return dict(message='Warning! No session.atate! WHY NOT??? variable missing after authorization.')

    if state != session.state:
        return dict(message='Warning! State mismatch. Authorization attempt may have been compromised. This: ' + state + ' should be: ' + session.state)


    # 2. Convert time-limited code
    # TODO: Replace values client_id, client_secret with YOUR's, previously received from dev.WordPress.com setup.
    payload = { 'client_id': CLIENT_ID, 
                'client_secret': CLIENT_SECRET,
                'redirect_uri': 'http://'+HOST+'/'+APPNAME+'/default/connected',
                'grant_type': 'authorization_code',
                'code': request.vars.code,
    }

    # Call the token endpoint
    rsp_token_request = requests.post("https://public-api.wordpress.com/oauth2/token", data=payload)

    # 'Response [200]' means SUCCESS
    if not 'Response [200]' in str(rsp_token_request):     
        return dict(message='Problem! "Response [200]" not in response!')

    jsonDict     = rsp_token_request.json()

    access_token = jsonDict['access_token']
    blog_id      = jsonDict['blog_id']
    blog_url     = jsonDict['blog_url']

    # TODO: [NO SHOWN] Store access_token, blog_id, blog_url in the db for further use.   

    # For demo purposes, we will use these credentials here, to list this blog.
    base_url = 'https://public-api.wordpress.com/rest/v1/sites/' + blog_id
    payload = {}
    payload['http_envelope'] = 'true'
    url_values = urllib.urlencode(payload)
    full_url = base_url + '?' + url_values
    req = urllib2.Request(full_url)   
    req.add_header('Authorization', 'Bearer ' + access_token)
    rsp = urllib2.urlopen(full_url)
    the_info_page = rsp.read()

    m = ''    
    m += "the_info_page = %s||"%(the_info_page)

    content = BEAUTIFY(m.split('||'))   

    return dict(content=content)     

models

I omit model code in this article, assuming you can store your credentials for further use. If you needd assistance, contact me by a comment.

views/default

connected.html

{{left_sidebar_enabled,right_sidebar_enabled=False,False}}
{{extend 'layout.html'}}

{{if 'message' in globals():}}
<h3>{{=message}}</h3>
{{elif 'content' in globals():}}
{{=content}}

{{pass}}

#api, #oauth2, #python, #rest, #web2py, #wordpress

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