Python: Unique Username, Groupname, Email Validators for TurboGears 2.0 Form Widgets
drks — Thu, 2009-12-31 00:34
Disclaimer: This is a rough brain dump. I plan on doing a proper hello world in the near future, but wanted to get this somewhere accessible so I didn't forget about it.
I came across a rather annoying issue of how to properly handle duplicate usernames, groupnames, and email addresses in turbogears 2. What I came up with was a set of validators that can be added to chained_validators on your form validation. I'm using the dMirr project as an example of how I'm using these validators:
dmirr/widgets/validators.py:
import re import pylons from pylons.i18n import ugettext as _ from formencode import Invalid from formencode.schema import SimpleFormValidator from tw.forms.validators import Email from dmirr.model import DBSession as db from dmirr import model __all__ = ['UniqueEmail', 'UniqueUserName', 'UniqueGroupName'] def validate_unique_email(value_dict, state, validator): # first for edit forms if value_dict.has_key('user_id'): u1 = db.query(model.User).filter_by(user_id=value_dict['user_id'])\ .first() if u1.email_address != value_dict['email_address']: u2 = db.query(model.User)\ .filter_by(email_address=value_dict['email_address']).first() if u2: return {'email_address':'The address already exists.'} # or new form else: u1 = db.query(model.User)\ .filter_by(email_address=value_dict['email_address']).first() if u1: return {'email_address':'The address already exists.'} def validate_unique_user_name(value_dict, state, validator): # first for edit forms if value_dict.has_key('user_id'): u1 = db.query(model.User).filter_by(user_id=value_dict['user_id'])\ .first() if u1 and u1.user_name != value_dict['user_name']: u2 = db.query(model.User)\ .filter_by(user_name=value_dict['user_name']).first() if u2: return {'user_name':'The user name already exists.'} # or new form else: u1 = db.query(model.User)\ .filter_by(user_name=value_dict['user_name']).first() if u1: return {'user_name':'The user name already exists.'} def validate_unique_group_name(value_dict, state, validator): # first for edit forms if value_dict.has_key('group_id'): g1 = db.query(model.Group).filter_by(group_id=value_dict['group_id'])\ .first() if g1 and g1.group_name != value_dict['group_name']: g2 = db.query(model.Group)\ .filter_by(group_name=value_dict['gropu_name']).first() if g2: return {'group_name':'The group name already exists.'} # or new form else: g1 = db.query(model.Group)\ .filter_by(group_name=value_dict['group_name']).first() if g1: return {'group_name':'The group name already exists.'} UniqueEmail = SimpleFormValidator(validate_unique_email) UniqueUserName = SimpleFormValidator(validate_unique_user_name) UniqueGroupName = SimpleFormValidator(validate_unique_group_name)
The easiest way to use this is to add the validator to your chained_validators under your form widget.
dmirr/widgets/user_form.py:
"""User Form""" import re from pylons.i18n import ugettext as _ from tg import config, url from tw.api import CSSLink from tw.forms import TableForm, TextField, TextArea, CheckBox, Spacer, \ HiddenField, PasswordField, Label, SubmitButton, Button from tw.forms.validators import Schema, Int, NotEmpty, UnicodeString, \ FieldsMatch, Email, URL from dmirr.widgets.validators import UniqueEmail, UniqueUserName class DynamicLabel(Label): value = '' template = 'dmirr.widgets.templates.dynamic_label' class UserNewForm(TableForm): css = [CSSLink(link=url('/theme/%s/_css/user.css' % config['theme']))] validator = Schema( chained_validators=[ FieldsMatch('email_address', 'confirm_email'), FieldsMatch('password', 'confirm_password'), UniqueEmail(), UniqueUserName(), ] ) fields = [ TextField('user_name', label_text='User Name', help_text="This can not be changed.", validator=UnicodeString(not_empty=True)), TextField('display_name', label_text='Display Name', validator=UnicodeString(not_empty=True)), TextField('web_url', label_text='Web URL', validator=URL(not_empty=False)), Spacer(), TextField('email_address', label_text='Email Address', validator=Email(not_empty=True)), TextField('confirm_email', label_text='Confirm Email'), Spacer(), PasswordField('password', label_text='Password', validator=UnicodeString(not_empty=True)), PasswordField('confirm_password', label_text='Confirm Password'), Spacer(), TextArea('about', label_text='About/Description'), Spacer(), TextArea('terms', label_text='Terms of Use'), CheckBox('agreed_to_terms', label_text='Agree to Terms', help_text="Check if you agree to the terms of use.", validator=NotEmpty), ] submit_text = 'Register User' class UserEditForm(TableForm): css = [CSSLink(link=url('/theme/%s/_css/user.css' % config['theme']))] validator = Schema( chained_validators=[ FieldsMatch('password', 'confirm_password'), UniqueEmail(), UniqueUserName(), ] ) fields = [ HiddenField('user_id', validator=Int()), HiddenField('user_name'), HiddenField('_method'), #TextField('user_name', label_text='User Name', # validator=UnicodeString(not_empty=True)), DynamicLabel('user_name', label_text="User Name", suppress_label=False), TextField('display_name', label_text='Display Name', validator=UnicodeString(not_empty=True)), TextField('web_url', label_text='Web URL', validator=URL(not_empty=False)), TextField('email_address', label_text='Email Address', validator=Email(not_empty=True)), Spacer(), PasswordField('password', label_text='Password', validator=UnicodeString(not_empty=False)), PasswordField('confirm_password', label_text='Confirm Password'), Spacer(), TextArea('about', label_text='About/Description'), SubmitButton('submit', attrs=dict(value="Save User")), Button('Cancel', attrs=dict(value="Cancel", onClick="history.back();")) ] create_user_form = UserNewForm("create_user_form", action=url('/user/')) edit_user_form = UserEditForm("edit_user_form", action=url('/user/?_method=PUT'))
You can see that I use both the standard Email validator in the widget, and also the new UniqueEmail, UniqueUserName validators in the chained_validators. All that is required is a hidden 'user_id' field in your form so that the validator can look up the current values in the database to compare against.
RSS Feed
Post new comment