Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask: conditional validation on multiple form fields

Let me start with usual - I'm new to both Python & Flask. Before posting this question (my first ever here) I have spent number of hours searching and experimenting, unfortunately with no luck so far.

I am building web form where user can define firewall rules, which subsequently get recorded in the database. I am at the validation stage and I came up to a wall here... hopefully someone will be able to help me out.

My (simplified here) form has 2 fields - src_ip and dst_ip:

class FirewallRule(Form)
    src_ip = StringField('Source IP')
    dst_ip = StringField('Destination IP')

and my validation requirements are:

  1. At least 1 field must be filled (both are also fine)
  2. Fields that are filled must contain valid IP address

wtforms.validators.IPAddress() and custom validation function seems to be my friend, but I am struggling to find a way to plug them together.

Essentially I am trying to build conditional validation:

  1. If not (src_ip OR dst_ip); return False
  2. If src_ip AND src_ip not valid IP address; return False
  3. If dst_ip AND dst_ip not valid IP address; return False
  4. Return True

Obviously I would like to re-use IPAddress() [or generally any of the built-in validators] rather then write my own.

I am sure someone must have done it before.. unfortunately I could not find any pointers in the right direction.

Thanks in advance.

like image 615
Sergiusz Avatar asked Apr 17 '15 15:04

Sergiusz


1 Answers

You are missing one very useful validator: Optional. This validator allows you to say that a field can be empty, but if it isn't empty then other validators should be used.

The part about having at least one of the fields filled out I would do with a custom validation method, I don't think there is any stock validators that can help with that.

So it would be something like this:

class FirewallRule(Form)
    src_ip = StringField('Source IP', validators=[Optional(), IPAddress()])
    dst_ip = StringField('Destination IP', validators=[Optional(), IPAddress()])

    def validate(self):
        if not super(FirewallRule, self).validate():
            return False
        if not self.src_ip.data and not self.dst_ip.data:
            msg = 'At least one of Source and Destination IP must be set'
            self.src_ip.errors.append(msg)
            self.dst_ip.errors.append(msg)
            return False
        return True

If you want to avoid a custom validation function, then consider that it would be fairly easy to create a validator class to check that at least one of a list of fields are set. You can look at the implementation of the EqualTo validator for inspiration if you would like to follow this route.

like image 153
Miguel Avatar answered Sep 22 '22 13:09

Miguel