views:

46

answers:

3

I'm performing all my form validation in a class, and would like to be able to get the errors from the class to the rendered html. One approach I was thinking about was to create a global variable "c" that would store all the errors and to set them from within the class, as I still want the individual methods to return false when they fail. Here is some sample code:

class User():

def add(self):

    #Check that e-mail has been completed
    try:
        #Validate e-mail address
        if (isAddressValid(self.email)):
            c.error = 'Invalid e-mail address'
            return 0
    except NameError:
        c.error = 'No e-mail address specified'
        return 0

Is there a better or preferred way to do this?

Thanks.

+1  A: 

I like to use a dictionary to hold the errors and warnings. Then I can either show all errors at the top of the form or inline. I also define error and warning variables so I can easily tell the two apart.

class User(object):
    def __init__(self):
        self.messages = {}

    def add(self):
        error = False
        warning = False

        #Check that name has a space
        try:
            if (self.name.find(' ') == -1):
                warning = True
                self.messages['email'] = {'type': 'warning',
                                          'msg': 'Your name has no space.'}
        except NameError:
            error = True
            self.messages['email'] = {'type': 'error',
                                      'msg': 'You have no name.'}

        #Check that e-mail has been completed
        try:
            #Validate e-mail address
            if (isAddressValid(self.email)):
                error = True
                self.messages['email'] = {'type': 'error',
                                          'msg': 'Invalid e-mail address'}
        except NameError:
            error = True
            self.messages['email'] = {'type': 'error',
                                      'msg': 'No e-mail address specified'}

        return error, warning
Robert Kluin
+1  A: 

Yes, definitely, and my suggestion is to avoid returning status codes at all.

Generally speaking, there is a lot of literature against using status codes and global variables to hold details for handling errors in a high level environment like Python.
Ned Batchelder has written a very good article on this topic; I strongly suggest you reading that page for a through lists of reasons why exception handling is usually considered a superior method.

But, as we are talking about Python, the official way to communicate exceptions, and errors, is through exception handling. Period.
Using any other way, will make your code against the common expectations for Python code, meaning it will be harder to read, and maintain.

Roberto Liffredo
Do you have any sample code to illustrate, for example, checking for whitespace characters in a name field and validating an e-mail address using exceptions instead of returning status codes? Thanks.
ensnare
+1  A: 

In the context of a web application you could just populate tmpl_context.

from pylons import tmpl_context as c
from yourproject.lib.base import BaseController, render

class MyController(BaseController):
    def index(self):
        c.error = 'Invalid e-mail address'
        return render('/mytemplate.mako')

Where 'mytemplate.mako' file content is:

% if c.error:
    error: ${c.error}
% endif

In generic python code you can:

Return a tuple

You can return a tuple from your function (it is not preferable way):

class Error(Exception):
    pass

def isvalid(something):
    return False, Error("'%s' is invalid" % (something,))

Example:

ok, err = isvalid(object())
if not ok:
   print err

Raise an exception

If an immediate caller is not supposed to handle an error from your function then an exception can be used to pass information about the error up the stack.

def do_stuff(something):
    if not something.isready():
       raise Error("'%s' is not ready to do stuff" % (something,))

Example:

class C(object):
    def isready(self):
        return False

def run():
    # no error handling here
    do_stuff(C()) 
    # do something else

try: run()
except Error, e:
    print e

Pass callback

def do_stuff(something, onerror=lambda err: None):
    if not something.isready():
       onerror(Error("'%s' is not ready to do stuff" % (something,)))

Example:

do = lambda: do_stuff(C(), onerror=repeat)
def repeat(err):
    """Repeat until success."""
    print err
    time.sleep(5) 
    do() # possible infinite loop 
do()
J.F. Sebastian
Thanks for the thorough answer.This is indeed for a web app. How would you go about passing errors from a method in a model to the controller and eventually to the template? I'd like to put all my error checking in the classes if possible. Thanks.
ensnare
@ensnare: You can use `validate` decorator as describe in http://wiki.pylonshq.com/display/pylonsdocs/Form+Handling#validation-the-quick-way
J.F. Sebastian