First Base Class

The previous step showed how to do the basics: let ZPT do most of the work and apply lxml.html to shove values in.

Obviously the previous step was very hard-wired. In this step we make a base class for the form that starts to move some of the work out of the view and into a form. We won’t, in this step, introduce the concept of multiple fields with declarative validators. But we will follow some of the same jargon we’ll use later with FormEncode schemas.

Plug in the Base Class

  1. Add a module for a BaseForm at t3/baseform.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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    """Base class for KARL3 forms.
    """
    
    from lxml.html import document_fromstring
    from lxml.html import tostring
    from time import time
    
    class BaseForm:
    
        submit_name = 'form.submitted'
        max_age = 200
    
        def __init__(self, request):
            self.request = request
            self.start_time = time()
    
        @property
        def is_submitted(self):
            return self.request.POST.get('form.submitted', False)
    
        def validate(self, fieldvalues):
            """Look at various fields and update values and errors"""
    
            fielderrors = {}
            for f in fieldvalues.keys():
                fielderrors[f] = None
            age = self.request.POST.get('age', False)
            if age is False or age == '':
                fielderrors['age'] = 'Age is required'
            try:
                if int(age) > self.max_age:
                    msg = 'Age must not be over %s' % self.max_age
                    fielderrors['age'] = msg
                fieldvalues['age'] = age
            except ValueError:
                fielderrors['age'] = 'Age must be an integer'
                fieldvalues['age'] = age
    
            return fieldvalues, fielderrors
    
        def render(self, htmlstring, fieldvalues):
            # Merge in field values, either from the default or from what
            # they typed in.
    
            form_tree = document_fromstring(htmlstring)
            form = form_tree.forms[0]
    
            for field_name, field_value in fieldvalues.items():
                form.fields[field_name] = unicode(field_value)
            merged_form_html = tostring(form)
    
            # Record the elapsed time
            self.elapsed = str(1 / (time() - self.start_time))[0:7]
    
            return merged_form_html
    
    • Sure, we’re hardwiring in a bunch of stuff as class attributes.
    • The constructor gets the request passed into it.
    • An is_submitted property lets us know if the form was posted or not. If not, the view can skip much of the work.
    • A validate method does some logic and updated formerror and fielderrors. It also updated fieldvalues, as a way to let the outside system be told when to shove in the default value or a transient value.
    • The render method uses lxml.html to shove the correct form values into the correct fields.
  2. The t3/views.py module gets a lot simpler:

     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
    32
    from repoze.bfg.chameleon_zpt import render_template_to_response
    from repoze.bfg.chameleon_zpt import get_template
    from repoze.bfg.chameleon_zpt import render_template
    from repoze.bfg.view import static
    from baseform import BaseForm
    
    static_view = static('templates/static')
    
    def my_view(context, request):
        layout = get_template('templates/layout.pt')
        form = BaseForm(request)
    
        fieldvalues = {'age': 40}  # Set a default age
        if form.is_submitted:
            fieldvalues, fielderrors = form.validate(fieldvalues)
        else:
            fielderrors = {'age': False}
    
        form_html = render_template(
            'templates/myform.pt',
            request=request,
            fielderrors=fielderrors,
            )
        rendered_form = form.render(form_html, fieldvalues)
    
        return render_template_to_response(
            'templates/mytemplate.pt',
            request = request,
            layout=layout,
            form_html=rendered_form,
            elapsed=form.elapsed,
            )
    
  3. The t3/templates/myform.pt form becomes more generic when grabbing errors:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    <html xmlns:tal="http://xml.zope.org/namespaces/tal">
    
      <form method="post" action="${request.url}">
        <fieldset tal:define="error fielderrors['age']">
          <label>Age <span class="required">&nbsp;</span></label>
          <input name="age"/>
          <div tal:condition="error" class="error">${error}</div>
        </fieldset>
        <fieldset>
          <input type="submit" name="form.submitted"/>
        </fieldset>
      </form>
    
    </html>
    

Table Of Contents

Previous topic

Simple Validation

Next topic

Getting Closer to FormEncode

This Page