Niklas Rosencrantz
Niklas Rosencrantz

Reputation: 26647

Validating many fields as they were one?

My input is rather strangely formatted but according to spec:

enter image description here

User should input the ID in 4 fields(!) where the id is the form 460 000 005 001 where 46 is a country code and the right part is the appengine ID of the user. Now I want to add validation to this field but I can't do it with wtforms. Here is my form class:

class RegisterWTForm(Form):
    soc_sec = TextField(_('Soc security number'),  [validators.Regexp('^[0-9]+$',
                      message=_('This is not a social security number, please see the example and try again'
                      )), validators.Required(message=_('Social security number is required')), unique_soc_sec], widget=MyTextInput())
    email = TextField(_('Email'),
                      [validators.Required(message=_('Email is required'
                      )),
                      validators.Email(message=_('Your email is invalid'
                      ))], widget=MyTextInput())

    def validate_soc_sec(form, field):
        if len(field.data) != 10:
            raise ValidationError(_('Soc sec must be 10 characters'
                                  ))
    def validate_email(form, field):
        if len(field.data) > 60:
            raise ValidationError(_('Email must be less than 60 characters'
                                  ))

And here is how I use the variables now, they are not part of the WTForm but regular http post parameters:

def post(self):
    Log1 = self.request.POST.get('Log1')
    Log2 = self.request.POST.get('Log2')
    Log3 = self.request.POST.get('Log3')
    Log4 = self.request.POST.get('Log4')    
    form = RegisterWTForm(self.request.params)
    if form.validate():
        logging.info('validated successfully')
    else:
        logging.info('form did not validate')
        self.render_jinja('register.html', form=form, Log1=Log1, Log2=Log2, Log3=Log3, Log4=Log4 )
        return ''
    email = self.request.POST.get('email')

    sponsor_id = '%s%s%s' % (Log2, Log3, Log4)
    if sponsor_id:
        user = User.get_by_id(long(sponsor_id))
    else:
        return 'Sponsor with sponsor id %s does not exist' % sponsor_id
    if not user:
            return 'Sponsor with sponsor id %s does not exist' % sponsor_id

    # make a token for a one-time login link that sets the password
    # Passing password_raw=password so password will be hashed
    # Returns a tuple, where first value is BOOL. If True ok, If False no new user is created

    user = self.auth.store.user_model.create_user(email)

Is there a way to add my 4 variables as one variable to my WTForm and add validation there, or should I make my own custom validation for these fields? For my other fields I've made them with a red border and red text if the field does not pass validation and I'd like to do the same here but it will be a lot of logic in the presentation layer if I can't do it with the form class and must add the code to the template. Can you propose what to do?

Thank you

Update

I could create a FormField that combines the fields but they don't render correctly and I don't need all 4 validations. Maybe you can tell me more how to make this they way I want?

class SponsorWTForm(Form):
    log1 = IntegerField('log1', [validators.required()])
    log2 = IntegerField('log2', [validators.required()])
    log3 = IntegerField('log3', [validators.required()])
    log4 = IntegerField('log4', [validators.required()])

class RegisterWTForm(Form):
    sponsor_id = FormField(SponsorWTForm)

...

enter image description here

Upvotes: 4

Views: 2470

Answers (1)

perelman
perelman

Reputation: 1777

It looks like WTForms solves this using field enclosures. The example at that link is for a telephone number with separate area code and number field fields which is very similar to your multi-part ID number use case.

To properly render the subform, you will have to define a custom widget. Look at the source code for ListWidget. Your widget will probably similar but simpler (as I think you just want a space between the different fields, not HTML tags). Something as simple as

return HTMLString(u' '.join([subfield() for subfield in field]))

may suit your needs. Then put widget=SingleLineWidget() (or whatever you name your widget class) in the arguments of the FormField constructor.

Slightly off-topic, the corresponding way to do this in Django Forms is rather different: you define custom clean and validate methods which transform your data (i.e. in this case, combining the 4 ID fields into one).

Upvotes: 5

Related Questions