Respect Validation in Python

In this article, I will tell you the story “how I got here” and we will look at the main advantages of this library. All useful links can be found at the end of the article.

History of creation

6 or 7 years ago I met the library respect/validation in PHP. Was delighted with its architecture and ease of use, it still works in several of my projects. Years passed and I moved to Python. Sometimes, you have to write an API, which means validating the data that the user sent. And now validation became a pain for me, I tried several libraries and no one could give me simplicity and flexibility (my opinion may differ from the opinion of other people :)), I tried to write something of my own, but everything ended in fiasco. In the end, I decided not to “reinvent the wheel”, but simply rewrite my favorite library for another language. The decision turned out to be successful, I have already begun to integrate it into my projects – I’m happy as an elephant.

Main advantages

Next, I will present you with a list of benefits. The advantages may seem abstract to you, because I will not compare this library with others, so as not to offend anyone.

  • Rule chains. Consider an example:


    You can easily follow the validation logic by looking at the chain of rules. From left to right – the value must have a data type strcontain only letters or numbers, length from 4 to 64.

  • No additional imports – you don’t need to import additional packages or modules, just import one validator module:

    from respect_validation import Validator as v

    And no dictionaries with validation rules.

  • The library has several logical operators that can be used to create validation chains. Operators:

# AllOf
# Все указанные цепочки правил должны быть удослетворены
v.AllOf(v.stringType(), v.alnum(), v.noWhitespace().length(4, 64)).validate('')
# Результат: False

# AnyOf
# Если любая из указанных цепочек правил будет валидна, 
# то закончить проверку удовлетворительно
v.AnyOf(v.stringType(), v.alnum(), v.noWhitespace().length(4, 64)).validate('')
# Результат: True

# NoneOf
# Если никакая цепочта не удовлетворяет условия,
# то закончить проверку удовлетворительно
v.NoneOf(v.stringType(), v.alnum(), v.noWhitespace().length(4, 64)).validate('')

# OneOf
# Только одна цепочка правил должна быть удовлетворена
v.OneOf(v.stringType(), v.alnum(), v.noWhitespace().length(4, 64)).validate('')
# Результат: True

# When
# Аналог if .. then .. else
	v.stringType(),  # если тип str, то проверить .. иначе перейти к другой цепочке
  v.alnum().length(4, 64),  # строка содержит только буквы и цифры, в пределах указанной длины
  v.intType().max(100),  # число с максимальным значением 100
# Результат: True
  • Operator Not I want to pay special attention. Any chain of rules can be inverted using this operator. At the same time, the displayed messages will also change if the check is not passed (about messages a little further).

# Результат: True
  • I also want to highlight the rules Optional and Nullable. These rules help me filter data for the database – when allowed Nonebut if there is something, then please follow the rules.

# Optional
# Если значение None или '', то игнорировать цепочку правил внутри
v.optional(v.alpha().length(3, 10)).validate('')
# Результат: True

# Nullable
# Если значение None, то игнорировать цепочку правил внутри
v.nullable(v.alpha().length(3, 10)).validate(None)
# Результат: True
  • At the moment, the library contains more than 130 rules, they should be enough for standard checks. Important! Most Rules can work with multiple data types so if you know exactly what type of data should be, then I recommend start the chain of rules with the indication of the data type. Full list of rules here.

  • It is very easy to create your own set of rules and use it as needed. I want to emphasize again, not a class, not a method, namely package (package) of rules or several packages. More information about this feature here.

  • Error messages. All standard rules already have error message templates. And there are at least two of them for each rule! The first one is standard and the second one is just in case you want to use the operator Not for this rule. There are several types of test results:

    • True/Falsewhen using the method .validate(input_value)

    • Return an exception on the first unsatisfied rule, or None (on success). To do this, use the method .check(input_value)

    • Return an exception following all the rules or None (on success). To do this, use the method .claim(input_value)

    In this case, we will consider the last example, using the method claim():

    v.alnum().noWhitespace().length(1, 15)\
    .claim('really messed up screen#name')
except NestedValidationException as exception:
		# у класса NestedValidationException есть два метода 
    # для сбора сообщений об ошибках
    # первый get_full_message(), вернёт нам сообщения в виде
    # отформатированного текста
    # get_messages() вернёт нам словарь, где ключи (keys) 
    # это имена правил, которые были не удовлетворены


# get_full_message()
- All of the required rules must pass for "really messed up screen#name"
  - "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
  - "really messed up screen#name" must not contain whitespace
  - "really messed up screen#name" must have a length between 1 and 15
 # get_messages()
  'alnum': ['"really messed up screen#name" must contain only letters (a-z) and digits (0-9)'], 
  'noWhitespace': ['"really messed up screen#name" must not contain whitespace'], 
  'length': ['"really messed up screen#name" must have a length between 1 and 15']

flask integration

As a bonus, I’ve added an additional FormValidation class that can make the process of validating incoming data easier. In my practice, in most cases, the frontend sends json, which is further transformed into a dictionary (dict). It is the resulting dictionary that can be passed through the FormValidator and get understandable messages that can then be broadcast to the user on the web form. Flask and FormValidator example here.


As a result, I got the desired result, now I have “the same” library.

Python and PHP have a lot of differences, but each of the languages ​​​​is beautiful in its own way, with its own “zest”. They have different logic, hence different approaches to solving problems, at certain moments. That is why the architecture of the original library was saved, but the implementation has a lot of differences, although when used you should get the same result.

The library went through a number of tests pytest (execution logic check), flake8 (syntax check) and mypy (data type check), which also served as a good experience.





Similar Posts

Leave a Reply