0

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.

  • If you just want to check if an integer has been entered, you can use an [IntegerField](https://wtforms.readthedocs.io/en/3.0.x/fields/#wtforms.fields.IntegerField) with the validators [InputRequired](https://wtforms.readthedocs.io/en/3.0.x/validators/#wtforms.validators.InputRequired) and [NumberRange](https://wtforms.readthedocs.io/en/3.0.x/validators/#wtforms.validators.NumberRange). If you want to query the content of another field within a custom validator, you can do this using the form that is passed with `self`. What exactly is your intention or goal? – Detlef Jun 26 '22 at 21:31
  • hi, I don't use IntegerField because if i don't enter anything I'm getting error "value is not integer" if i want to use it, i have to pre populate Integer fields. so i opted to just use StringFields and convert values to integers if its possible. No i cant use pre build validators, because if salarylow and salary_high fields are populated, then salary_fixed field must remain empty and vise versa. – Rugile Venskute Jun 27 '22 at 03:34

2 Answers2

0

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!

E. Turok
  • 106
  • 1
  • 7
  • Yes i know that, and if i remove it, it works fine, the problem is that i need that validate_salary_fixed function know if there is some values in validate_salary_high function field and vice versa. both of those fields have to look at each other to see if there is a value – Rugile Venskute Jun 26 '22 at 21:04
0

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())
Detlef
  • 6,137
  • 2
  • 6
  • 24
  • I already tried to use Optional(), but if I use custom functions, I can run that function, but for some strange reason I cant rise `ValidationError()` – Rugile Venskute Jun 27 '22 at 16:05
  • `Optional` allows empty inputs and stops the validation chain from continuing. Maybe that's the reason. The order of the validators is crucial. For this reason I also overwrote the IntegerField. – Detlef Jun 27 '22 at 18:39