Trying to compare 3 wtforms fields with custom functions and geting recursion problem

43 views Asked by At

I have 3 fields that I want to compare salary "from" field and "to" field and also there is fixed salary field. I have no idea how to do it, since there is no documentation how to do it, so i created custom function that look to each other and trying to se if they have a value.

def validate_salarylow(self, salarylow):
        if self.validate_salary_fixed(self.salary_fixed) != "":
               salarylow.data = int(0)   
        else:
            try:
                salarylow.data = int(salarylow.data)
            except:
                raise ValidationError("value is not a number")
        return salarylow.data

    def validate_salary_high(self, salary_high):      
        if self.validate_salary_fixed(self.salary_fixed) != "":
               salary_high.data = int(0)      
        else:
            try:
                salary_high.data = int(salary_high.data)
            except:
                raise ValidationError("value is not a number")
        return salary_high.data       

    def validate_salary_fixed(self, salary_fixed):
        if self.validate_salary_high(self.salary_high) != "":
               salary_fixed.data = int(0)
        try:
            salary_fixed.data = int(salary_fixed.data)   
        except:
            raise ValidationError("value is not a number")
        return salary_fixed.data 

if I don't set if self.validate_salary_high(self.salary_high) != "": everything works fine. but when i set it I'm getting "RecursionError: maximum recursion depth exceeded" error.validate_salary_fixed function looks to validate_salary_high function and vice versa. I'm new in Python and flask and I'm sure there is easy solution, but I cant find it so I would appreciate if anyone could help.

2

There are 2 answers

2
Detlef On BEST ANSWER

My suggestion is to suppress the error message of the integer field by overwriting it. Thus, the types of the inputs do not have to be converted.
For validation I use two custom validators, one of which checks whether a range or a fixed value has been entered and the second checks the range for its limits. In addition, pre-built validators are used to prohibit negative values.
I'm not sure if you really need the field for the fixed salary, because it is possible to define a fixed value by narrowing the range.

from flask_wtf import FlaskForm
from wtforms import IntegerField
from wtforms.validators import (
    NumberRange,
    Optional,
    StopValidation,
    ValidationError
)

class OptionalIntegerField(IntegerField):
    def process_data(self, value):
        try:
            super().process_data(value)
        except ValueError:
            pass

    def process_formdata(self, valuelist):
        try:
            super().process_formdata(valuelist)
        except ValueError:
            pass

def validate_salary(form, field):
    range_fields = [form.salary_low, form.salary_high]
    if all(f.data is None for f in [form.salary_low, form.salary_high, form.salary_fixed]) or \
        (form.salary_fixed.data is not None and any(f.data is not None for f in range_fields)) or \
        (form.salary_fixed.data is None and any(f.data is None for f in range_fields)):
        raise StopValidation('Either state a range from low to high or a fixed salary.')

def validate_salary_range(form, field):
    if form.salary_low.data and form.salary_high.data and \
        form.salary_low.data > form.salary_high.data:
        raise ValidationError('The lower value should be less than or equal to the higher one.')

class SalaryForm(FlaskForm):
    salary_low = OptionalIntegerField(
        validators=[
            validate_salary,
            validate_salary_range,
            Optional(),
            NumberRange(min=0)
        ]
    )
    salary_high = OptionalIntegerField(
        validators=[
            validate_salary,
            validate_salary_range,
            Optional(),
            NumberRange(min=0)
        ]
    )
    salary_fixed = OptionalIntegerField(
        validators=[
            validate_salary,
            Optional(),
            NumberRange(min=0)
        ]
    )

app = Flask(__name__)
app.secret_key = 'your secret here'

@app.route('/', methods=['GET', 'POST'])
def index():
    form = SalaryForm(request.form)
    if form.validate_on_submit():
        print(form.salary_low.data, ' - ', form.salary_high.data, '||', form.salary_fixed.data)
    return render_template('index.html', **locals())
1
E. Turok On

Let's take a look at your code:

  1. Your function validate_salary_high calls validate_salary_fixed.
  2. But when you go to your function validate_salary_fixed it calls validate_salary_high.
  3. So you go back to your function validate_salary_high which calls validate_salary_fixed.
  4. Now in your function validate_salary_fixed, you call validate_salary_high.

Your functions repeatedly call each other over and over again, forever, until your computer eventually throws an error - and this is exactly what is happening to you.

The way to get around this is to remove one of your recursive calls. More specifically you should either

  1. remove your call to validate_salary_fixed in the function validate_salary_high
  2. or remove your call to validate_salary_high in the function validate_salary_fixed

You should chose which function call to remove depending on the goal of your code (which I don't fully understand.) Good luck!