Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask validates decorator multiple fields simultaneously

I have been using the @validates decorator in sqlalchemy.orm from flask to validate fields, and all has gone well as long as all of the fields are independent of one another such as:

@validates('field_one')
def validates_field_one(self, key, value):
   #field one validation

@validates('field_two')
def validates_field_two(self, key, value):
   #field two validation

However, now I need to do some validation that will require access to field_one and field_two simultaneously. It looks like validates accepts multiple arguments to the validates decorator, however, it will simply run the validation function once for each argument, as such:

@validates('field_one', 'field_two')
def validates_fields(self, keys, values):
   #field validation

Results in a work flow of validate field_one and then validate field_two. However, I would like to validate both at the same time(a trivial example of which would be assert that the value of field_one is not the value of field_two, an example of which would be disallowing self-loops in a graph where field_one and field_two refer to nodes and it is performing validation on an edge). How would be the best way to go about doing that?

like image 626
Brent Hronik Avatar asked Sep 14 '15 00:09

Brent Hronik


2 Answers

Order the fields in the order they were defined on the model. Then check if the last field is the one being validated. Otherwise just return the value unchecked. If the validator is validating one of the earlier fields, some of them will not be set yet.

@validates('field_one', 'field_two')
def validates_fields(self, key, value):
    if key == 'field_two':
        assert self.field_one != value
    return value

See this example.

like image 84
r-m-n Avatar answered Oct 23 '22 04:10

r-m-n


Adding another answer here, as the accepted one didn't quite meet my use case for using another field to validate and modify relationship/collection fields, which are not really compatible with @validates. In this case you can use an event listener for the before_flush event to achieve what you're looking for:

@event.listens_for(Session, 'before_flush')
def validate_and_modify_relationships(session, flush_context, instances):
    """
    Complex validation that cannot be performed with @valdiates
    """
    
    # new records only (for updates only, use session.dirty)
    for instance in session.new:
        if isinstance(instance, MyData):
            if instance.some_other_value:
                instance.some_relation = []

More details here: Flask-SQLAlchemy validation: prevent adding to relationship based on other field

like image 25
Brendan Avatar answered Oct 23 '22 06:10

Brendan