FormValid.py :  » Web-Frameworks » Aquarium » aquarium-2.3 » aquarium » util » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Aquarium 
Aquarium » aquarium 2.3 » aquarium » util » FormValid.py
"""This module contains classes to help validate user input.

Example usage, a simple one element form::
   
    from aquarium.util.FormValid import Form, Field

    (values, errors) = self.createValidator()(ctx.form)
    if errors:
        ctx.actionResults("Failed validation.")
        return
    # Now, use values instead of ctx.form, and continue on your merry way.

    def createValidator():
        return Form([
            Field("source_ip", "IP is invalid", 
                  regex=re.compile(r"\d+\.\d+\.\d+\.\d+"))
        ])

"""

__docformat__ = "restructuredtext"

# Created: Mon May  3 13:55:15 PDT 2004
# Author: Bob VanZant, Shannon -jj Behrens
# Email: bob@norcalttora.com, jjinux@users.sourceforge.net
#
# Copyright (c) Bob VanZant.  All rights reserved.

# TODO: 
#   - Add support for multiple fields having same name (This is sort of already
#      handled. Just never been tested.
#   - Add support for per-Form and per-Field filters
#

import re
import types


class FieldInvalid(Exception):
    """Raise this when a field does not meet the proper type."""
    def __init__(self, fields, *args):
        Exception.__init__(self, *args)
        self.fields = fields

######################################
class Form:
    """This class uses the member variable "constraints" as a list to store
       elements of type class ``Field``. The form then represents a form of
       user input controls, perhaps an HTML form. Add fields to this class,
       call the ``validate()`` method and let the magic begin.

       It is expected that you will usually just subclass this class and modify
       ``self.constraints`` at that time. Obviously you don't *need* to do
       this.

       Setting up rules is done when you add a Field and is documented there
    """

    def __init__(self, constraints):
        """ A Default constructor that is not often called. """
        self.constraints = constraints
        
    def __call__(self, values):

        self.validate(values)
        return self.flattenResults()

    def validate(self, values):
        """The main magic worker. Loops over each of the field elements stored
           in ``self.constraints`` and calls its ``validate()`` method. Takes
           the results returned by the validation and updates its results dict.

           Returns a dictionary containing all of the fields we validated,
           indexed by field name.
        """
        self.result = {}

        for x in self.constraints:
            self.result.update( x.validate(values) )

        return self.result

    def isValid(self):
        """Call this after doing a ``validate()``. Returns true if the form is
        100% valid and false otherwise"""
       
        for key in self.result.keys():
            if self.result[key].invalid:
                return False
        # This used to use the stuff below. I don't quite no why
        #for x in self.constraints:
        #    if x.invalid:
        #        return False
       
        return True

    def flattenResults(self):
        values = {}
        errors = {}

        for name in self.result.keys():
            values[name] = self.result[name].value

            if self.result[name].invalid:
                if isinstance(self.result[name].invalid, dict):
                    for item in self.result[name].invalid.keys():
                        errors[name + "[%s]" %item] = \
                                self.result[name].invalid[item]
                else:
                    errors[name] = self.result[name].errorMsg

        if errors:
            return (None, errors)
        else:
            return (values, errors)


##############################################
class Field:
    """This class does the majority of the work. A field has many keyword
       args that can be set at instantiation that define how the field is
       validated. In addition there are two required parameters.

       Required parameters:

       name 
         The name of the field. Must be the same as the field name that is
         used as the index in the values dict that is passed in to validate
         against.

       errorMsg
         The error message to raise as part of the exception on failure.

       Optional parameters:
         
       cast 
         A function callback that will be used to attempt to cast a value
         before attempting to validate it.

       default
         If given, this is the default value to use for validation when the
         user did not specify one. To give the effect of an optional field in
         your form, set the default to "".

       function
         A function callback that will be used to validate this value.
         Expected to return true on success and false on failure.  Function
         should accept one argument, the value.

       regex
         A compiled regex object (i.e. the result of a ``re.compile()``) that
         will be used to validate the value against using ``re.match()``

       dependents
         A list of tuples of the form ``(regex, Form)``. If the value of this
         field matches the given regex we will attempt validation of ``Form``.
         Otherwise, ``Form`` is ignored. Since ``Form`` is ignored any data the
         user has entered into those fields will not be included in the results
         dict and will thus require extra work to send them back to the user.

       strip
         A boolean that defaults to True stating whether or not to ``.strip()``
         a string before attempting validation. Will catch ``AttributeError``
         and pass if you tried to ``strip()`` a non-string.

    """
      
    def __init__(self, name, errorMsg, **kargs):
        
        # input params
        self.name = name
        self.errorMsg = errorMsg
        self.options = kargs

        # regular old member vars
        self.value = ""
        self.result = {}
        self.invalid = False #innocent until proven guilty


    def __repr__(self):
        return (
            """<Field name="%s" inv="%s" errorMsg="%s" value="%s">\n""" %(
                    self.name, self.invalid, self.errorMsg,
                    self.value) )


    def validate(self, values):
        """Attempts to validate this field. Because of dependencies it is
           required that "values" be passed in as a dict object containing, at a
           minimum, access to values for all Fields in this Field and its
           dependencies.

           As a postcondition, ``self.result`` is a dictionary containing all
           of the ``Fields`` we validated (valid or invalid does not matter).
           The dict is indexed by field name and contiains a ``Field`` object.
           The value property represents the value after casting (if
           applicable).  Other properties include the ``errorMsg``, and whether
           or not the field is ``invalid``.

           Dependencies of a field, if validated, are returned as part of this
           dictionary. There is no nesting, so the multi-level paradigm used
           to enter a form is lost on return; the results are flattened.
        """

        # Try to validate the field, act accordingly on failure/success
        try:
            if values.get(self.name, None):
                self.value = values[self.name]

                def valid_helper():
                    self._strip()
                    self._cast()
                    self._validate_function()
                    self._validate_regex()

                if isinstance(self.value, dict):
                    self.invalid = {}
                    vals = self.value
                    finalValues = {}
                    for item in vals.keys():
                        self.value = vals[item]
                        try:
                            valid_helper()
                            finalValues[item] = self.value
                        except FieldInvalid:
                            self.invalid[item] = self.errorMsg
                    self.value = finalValues
                else:
                    valid_helper()

            elif self.options.has_key("default"):
                self.value = self.options["default"]
            else:
                # Try to cast it anyway, if the cast yields a good error
                # message, we'll use it, otherwise its no big deal.
                self._cast()
                raise FieldInvalid(self)
        except FieldInvalid:
            self.invalid = True
        
        result = {self.name : self}

        # Go validate dependencies
        result.update( self._validate_dependencies(values) )
         
        return result

    def _strip(self):
        """Strips value, if we're supposed to as defined by the strip karg."""

        if self.options.get("strip", True):
            try:
                self.value = self.value.strip()
            except AttributeError:
                pass

    def _validate_dependencies(self, values):
        """Validates dependencies, if any. Loop over the items in the
           dependents list, if the regex matches attempt validation of
           the form, bubble up the results. Otherwise, ignore the form.
        """
           
        result = {}
        
        if self.options.has_key("dependents"):
            for (condition, form) in self.options["dependents"]:
                if condition.match(self.value):
                    result.update( form.validate(values) )    
                
        return result

    def _cast(self):
        """Attempt to cast the data if we're supposed to"""
        if self.options.has_key("cast"):
            try:
                self.value = self.options["cast"](self.value)
            except:
                # Must be a catchall because we don't care what exceptions
                # get raised in the exception
                raise FieldInvalid( self )

   
    def _validate_function(self):
        """Call the function callback with ``self.value`` if it is defined.
           Function may also be a list of functions.
           The function should return True on success, False on failure.
           Function should not raise any exceptions that it does not
           catch itself.
        """
        if not self.options.has_key("function"):
            return

        # Handle a list of functions
        if isinstance(self.options["function"], types.ListType):
            for func in self.options["function"]:
                if not func(self.value):
                    raise FieldInvalid(self)
        
        # Handle a single function
        elif not self.options["function"](self.value):
            raise FieldInvalid(self)

        return


    def _validate_regex(self):
        """Attempt to match against the regex object, if it exists"""
        if not self.options.has_key("regex"):
            return
        if self.options["regex"].match(self.value):
            return
        else:
            raise FieldInvalid(self)
    
        
       

class FieldGroup:
    """This class allows you to group fields together and do validation
       based on the results of the validation of those fields

       name
         A string that represents the name of this group

       function
         A user-specified function callback that will be called with all of the
         values of the group (as a dict) iff each of the values of the group
         validate successfully on their own. To ensure that empty fields in a
         group validate, use the ``default`` karg when creating each ``Field``.
         This function need return a single string (error message) on error or
         None if the group is valid.
       
    """

    def __init__(self, name, function, fields, **kargs):
        self.name = name
        self.function = function
        self.fields = fields
        self.options = kargs
        
        self.invalid = False

    def validate(self, values):
        """Attempts validation of each of the Fields in this group. Returns
        results consistent with ``Field.validate`` and ``Form.validate``"""

        self.result = {}
        for field in self.fields:
            self.result.update( field.validate(values) )

        values = {}
        errors = {}
        for name in self.result.keys():
            values[name] = self.result[name].value
            if self.result[name].invalid:
                errors[name] = self.result[name].errorMsg

        # Only attempt to validate the group if all fields are valid
        if errors:
            self.invalid = True
            self.errorMsg = ""
        else:
            errorMsg = self.function(values)
            if errorMsg:
                self.errorMsg = errorMsg
                self.invalid = True

        self.result.update({ self.name : self })

        return self.result
    
    def __getattr__(self, name):
        if name == "value":
            return [ field.value
                     for field in self.fields ]
        raise AttributeError("%s not a valid attribute" %name)
  
def validatePassword(values):
    if values["password"] <> values["passwordConfirm"]:
        return "Passwords do not match"
    elif len(values["password"]) < 6:
        return "Password is too short"
    else:
        return None

def validateMessageBody(values):
    entered = values.get("msg_body", None)
    upload  = values.get("upload", None)

    if entered and upload:
        return "Please specify only one or the other"
    elif (entered and not upload) or (not entered and upload):
        return None
    else:
        return "You must either enter a message or select a file for upload"


reNumbersOnly = re.compile(r"[\d]")
reLettersOnly = re.compile(r"[\w_-]")
reAlphaNum    = re.compile(r"[\w\d_-]")
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.