All in one forms

June 2, 2010

This’s a quick topic that I felt I should write about today.

  • We should know that request.args(..) is better than request.args[..] why?
    because the former doesn’t raise an exception[error ticket] in case the element with the specified index is not there.
    How could this be important?Shouldn’t I get an error when trying to access something that is not there ? Yes and No ….

    In web2py you could do something like:

    def action():
       if not len(request.args):
           form = SQLFORM(db.table_name)
           if form.accepts(request.vars, session):
                response.flash = 'record created'
        else: # update form
            record = db(db.table_name == request.args[0]).select()
             if len(request.args) and len(records): 
            form = SQLFORM(db.table_name, request.args[0], deletable=True)
            if form.accepts(request.vars, session):
                response.flash = 'record updated'
        records = db(>0).select()
        return dict(form=form, records=records)
  • Now the question is :- do I really have to check for the request.args ?

    In fact in this simple case , I don’t…. unless i’m using request.args[0]
    which shall raise an exception if there’re no arguments…in the request.
    If I’m using request.args(0), I don’t have to check for the len() and again in my simple case .You may need it in other contexts.

  • Now another question: Do I’ve to make a database query like:
     record = db(db.table_name == request.args[0]).select()

    and the other check on len(records) in order to make an update form?

     if len(request.args) and len(records): 
            form = SQLFORM(db.table_name, request.args[0], ...)

    Ah …. see ? I’d to make sure that request.args in fact of length > 0
    and that request.args[0] is a valid record id in the target table.
    this’s in order to make an update form.

  • Yet another question: Don’t you all find that using 2 forms one for creating and the other for updating forms is a bet overhead [IN THIS SIMPLE CASE]?

    One can argue that it’s important because in the 2nd case of the update form one has to specify the record id , ‘deletable=True’, and ‘upload=URL(…)’ for the cases in which form has image fields.

    But, using request.args() makes things easier

  • In fact I can do something like:
    • ex:
       def index():    
          form = SQLFORM(db.table_name, request.args(0), deletable=True, upload=URL(r=request, f='download'))
          if form.accepts(request.vars, session):
              if not form.record: 
                  response.flash = 'record created'
                  if form.vars.delete_this_record: # form.vars.delete_this_record is True in case one submits form to delete a record
                      session.flash = 'record deleted'
                      session.flash = 'record updated'
                  redirect(URL(r=request, f='index'))            
          records = db().select(db.table_name.ALL)        
          return dict(form=form, records=records)
    • form.record is True in case of update form otherwise it’s False.True means it holds the id of the record intended to be updated.
      An update form having second argument as request.args(0) which is None in case there’re no arguments will be equivalent to a create form .
      Interesting right?!!! 😀
    • Warning:
      records = db().select(db.table_name.ALL)  

      should be in the end of the function so that when you submit a new record or update/delete a record effects will be reflected directly
      the database query is done after the form submission
      if you put it above all code.. code still works but when inserting a new record you’ve to make an extra refresh to see the effect in the table containing records

    • Why I do redirect after update?
      because after updating a form it keeps the values on it and I don’t like user to see the old records data after deleting a record …
      This has the effect of tempting user to hist submit again causing a ticket to be thrown because he’s trying to update a non existing record.
    • To complete the tutorial, I’ve to provide you with the view file
      {{extend 'layout.html'}}
      	{{for record in records:}}
      	{{filename, file = db.table_name.x.retrieve(record.x)}}
      		{{=A(IMG(_src=URL(r=request, c='default', f='download', args=[record.x])), _href=URL(r=request, c='default', f='index', args=[]))}}

  • Record deletion, keepvalues and field representation

    May 2, 2010

    Suppose you’ve 2 SQLFORMs to create/update records in a database table

    def reprs(value):
        result  = A(value, _href=URL(r=request, f='index', args=value))
        return result
    def index():
        if not request.args:
            form = SQLFORM(db.my_table)
            if form.accepts(request.vars, session, keepvalues=keepvalues):
                response.flash  = T('record created')
            form = SQLFORM(db.my_table, request.args(0), deletable=True)
            if form.accepts(request.vars, session):
                response.flash  = T('record created') = reprs
        records = db().select(db.my_table.ALL)
        return dict(form=form, records=records)
  • Now as you can see, we’ve some fun stuff :
    keepvalues when set to True when creating a record then the form will keep the values after successful creation of the new record and vice versa.
    keepvalues=True is very useful in successive insertion of similar data
  • In update forms, keepvalues is always set to True, even if you tried to set to False, it won’t work
    and this makes sense actually, you want to see the updated data after
    updating it.And any way this’s easily can be changed in the code in case you’ve another opinion.
  • The table_name.field_name.represent is cool as you can see, you can make the table you create contains a reference to update the record
    or to refer to another link [This depends on your needs]
  • In my opinion while the represent is a function that takes a string argument [field name], it would have been more useful if it could refer to the record as a whole, in this case you could make a field refer to a an action that may take as argument the value of another field.
    In the current situation you could do something like that by making a query based on the value of the field which is useless unless the field value is unique , so it’s more useful to be used with id fields in general.
    Moreover it could have taken the record, one will not be forced to make an extra query.
    Of course something like this can be done in other ways.
    If you’ve an argue about my point of view, let me know.
  • Another thing to notice is that when deleting a record, form variables should be cleared which is not the case here.
    In my openion this’s not a good idea in fact, and needs to be fixed because it’s really tempting to see the variables still in the form and makes one clicks the [‘submit’] button again to see what would happen
    and it returns ‘object not found’ error message
    One way to overcome this issue is to explicitly check whether user have asked to delete a record or not in the accepts() function.

    if form.accepts(request.vars, session):
        if form.vars.delete_this_record:
            #record is deleted then redirect user to the same page without  
              arguments [without the last argument if page takes many arguments]
            redirect(URL(r=request, f=....))

  • Quickly, getting a table of records

    April 28, 2010
  • Of course you know that something like:
    rows = db( > 0).slect()

    yields a Rows object that can be returned using :

    return dict(rows=rows)

    and in your view making something like:


    will get you a table , yes an HTML table created for you on the fly

  • The problem with this approach is that when you need more customization like decorating your table using java script or doing any other customization you’ll have to create the table manually in the view doing something like:
    <table class="blahblahblah">
    <th> {{T("Title'')}} </th>
    <th> {{T("Location'')}} </th>
    {{for row in rows:}}
    <td>{{row.title}} </td>
    <td>{{row.location}} </td>

    boring is not it ?

  • We can make another hackish trick which makes our lives easier:
    since Rows object has an attribute called colnames containing the column names of the rows being returned and since column names consists of table_name.field_name:
    you can do something like :

    x = db(>0).select()
    [column.split('.')[1] for column in x.colnames]
    ['id', 'title']

    Now you can do :

    ---- mytable.html ----
    {{def mytable(records, class_name=''):}}
    <table class="{{=class_name}}">
    {{for col in [column.split('.')[1] for column in x.colnames]:}}
    <th> col</th>
    {{for r in records:}}
    <tr>{{for k,v in r.items():}}
  • After that all you need to do when ever trying to make a table is to
    import mytable.html in your view with

     {{include 'mytable.html'}}

    and use it

    {{mytable(db(...).select(), 'class_name')}}

    then you can personalize your table as you want and reuse it.

  • Cool tools for web development

    April 28, 2010

    txt2tags   tinymce  pydns   libgmail  iodbc  win junction [sym links for ntfs on windows]   easy eclipse   Testing tools  yaml  web2py user group search plugin    ulipad   sphinx   firedaemon  edna [mp3 server] flot  sqldesigner and you can check it out here   
    Free layouts   JQueryUI  Breve   send sms  open laszlo  (disqus) An interesting comment system for your site    DotNet Nuke    helpful firefox plugin for developers   Merbivore   Cheetah   How to know visitor’s country  Python captcha extjs 
    JQuery live search 1 and hereopenlayers Change image to icon 
    yslow webx css library Rat Proxy