0

I am creating a form for the user to enter a valid UK postcode. I currently have the following code for the form:

class RegisterForm(Form):
    first_name = StringField('First Name', validators=[DataRequired(), validators.Length(min=1, max=50)])
    last_name = StringField('Last Name', validators=[DataRequired(), validators.Length(min=1, max=50)])
    centre_id = StringField('Centre ID', validators=[DataRequired(), validators.Length(min=1, max=11)])
    doctor_id = StringField('Doctor ID', validators=[DataRequired(), validators.Length(min=1, max=11)])
    address = StringField('Address Line 1', validators=[DataRequired(), validators.Length(min=1, max=100)])
    town_name = StringField('Town Name', validators=[DataRequired(), validators.Length(min=1, max=50)])
    county_name = SelectField('County Name', choices=[('antrim', 'Antrim'), ('armagh', 'Armagh'), ('down', 'Down'), ('derry/londonderry', 'Derry/Londonderry'), ('fermanagh', 'Fermanagh'), ('tyrone', 'Tyrone')], validators=[DataRequired()])
    postcode = StringField('Postcode', validators=[DataRequired(), validation.is_valid_postcode])
    telephone_number = TelField('Telephone Number', validators=[DataRequired(), validators.Length(min=11, max=11)])
    email_address = EmailField('Email Address', validators=[DataRequired()])
    patient_username = StringField('Username', {validators.DataRequired(), validators.EqualTo('confirm_patient_username', message='Usernames do not match')})
    confirm_patient_username = StringField('Confirm Username')
    patient_password = PasswordField('Password', {validators.DataRequired(),validators.EqualTo('confirm_patient_password', message='Passwords do not match')})
    confirm_patient_password = PasswordField('Confirm Password')

and the template is:

{% extends 'patient/patient_login_layout.html' %}

{% block body %}
    <h1>Patient Register</h1>
    {% from 'includes/_formhelpers.html' import render_field %}
    <form method="POST" action="">
        <div class="form-group">
            {{render_field(form.first_name, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.last_name, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.centre_id, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.doctor_id, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.address, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.town_name, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.county_name, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.postcode, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.telephone_number, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.email_address, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.patient_username, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.confirm_patient_username, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.patient_password, class="form-control")}}
        </div>
        <div class="form-group">
            {{render_field(form.confirm_patient_password, class="form-control")}}
        </div>
        <p><input type="submit" class="btn btn-primary" value="Submit"></p>
    </form>
{% endblock %}

but getting the error: TypeError: argument of type 'StringField' is not iterable.

What can I do to fix this?

Edit: Here are the complete form entry and the template for the webpage. Thanks a lot.

Julia
  • 397
  • 3
  • 14
fg2210
  • 61
  • 1
  • 7
  • Execution of this line causes the `TypeError`? – Julia Mar 07 '19 at 13:37
  • @Julia yes, TypeError: argument of type 'StringField' is not iterable – fg2210 Mar 07 '19 at 13:43
  • Could you provide a complete code of the form and the template? – Julia Mar 07 '19 at 13:52
  • @Julia changed the question there to include more detail – fg2210 Mar 07 '19 at 14:06
  • I've plugged the StringField's above into flask app I'm building and had no issues which suggests the error lies elsewhere such as you views.py. Does your stack show which line of code is causing the issue...as Julia says, more detail required. – Andrew Allen Mar 07 '19 at 14:39

1 Answers1

1

You are not using the WTForms validators correctly in your postcode StringField.

Declare a function that takes form and field parameters and raise an error if the post code is invalid, in this function you make use of the uk-postcode-utils is_valid_postcode function, i.e.:

class RegisterForm(Form):
    #  ...
    postcode = StringField('Postcode', validators=[DataRequired(), post_code_validator])

    from wtforms.validators import ValidationError
    from ukpostcodeutils import validation
    
    def post_code_validator(form, field):
        # raise a validation error IF the post code doesn't validate
        if not validation.is_valid_postcode(field.data):
            raise ValidationError('Invalid UK Post Code') 

Register the validator on the StringField:

class RegisterForm(Form):
    #  ...
    postcode = StringField('Postcode', validators=[DataRequired(), post_code_validator])
    # ...

If a post code field is being used in several forms you're better off writing a class validator as outlined in the WTForms documentation.

Something like (untested):

from wtforms.validators import ValidationError
from ukpostcodeutils import validation

class PostCodeValidator(object):
    def __init__(self, message=None):
        if not message:
            message = u'Must be a valid UK Post Code'
        self.message = message

    def __call__(self, form, field):
        # raise a validation error IF the post code doesn't validate
        if not validation.is_valid_postcode(field.data):        
            raise ValidationError('Invalid UK Post Code')

post_code_validator = PostCodeValidator
pjcunningham
  • 7,676
  • 1
  • 36
  • 49
  • It seems that the example shown in https://pypi.org/project/uk-postcode-utils/ is the other way around: if validation.: is_valid_postcode('SW1A1AA'): print "Postcode OK. The "if not" is missing – Rodolfo Alvarez Nov 03 '22 at 12:12
  • @RodolfoAlvarez - reverse logic. We want to raise a `ValidationError` *IF* the post code validation fails. – pjcunningham Nov 03 '22 at 17:28
  • yes you are right . That is clear. The statement in the example in the URL I mentioned before has a error . Please have a look a tried with an UK postcode – Rodolfo Alvarez Nov 04 '22 at 15:54