7
votes

I'm using Django 1.8.

The documentation on writing validators has an example of a function-based validator. It also says the following on using a class:

You can also use a class with a __call__() method for more complex or configurable validators. RegexValidator, for example, uses this technique. If a class-based validator is used in the validators model field option, you should make sure it is serializable by the migration framework by adding deconstruct() and __eq__() methods.

  • What are the pros/cons of class-based against function-based validators?
  • What is __call__() used for, and how is it used?
  • What is deconstruct() used for, and how is it used?
  • What is __eq__() used for, and how is it used?

An example would be helpful. A full answer may also be worth submitting to be in the official documentation.

Thanks!

3
Remember to accept the answer that was most helpful to you. - code_dredd

3 Answers

4
votes

A big advantage of validator functions is that they are really simple. They simply take one value as an argument, check that it's valid, and and raise ValidationError if it's not. You don't need to worry about deconstruct and __eq__ methods to make migrations work.

The validate_even example in the docs is much simpler than a validator class would be.

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError('%s is not an even number' % value)

If you need to check divisibility by other numbers as well, then it would be worth creating a validator class ValidateDivisibleBy. Then you could use ValidateDivisibleBy(2), ValidateDivisibleBy(3) and so on. But a lot of the time, a validator function is good enough.

2
votes

Other than being able to inherit from BaseValidator, there might not necessarily be a significant pro/con to choosing function vs class-based validators. I prefer class-based because you can keep internal state if necessary without making it visible to the clients (e.g. compiled regexes, pre-computed values in a table, history, etc.)

The __call__ method makes an object callable and allows it to sort of emulate function-like behavior (i.e. the object can be called like a function would), and the object's __call__ override will be invoked. It requires you to implement the special __call__(self, ...) method in the validator.

class Callable(object):
    def __call__(self,*args,**kwargs):
        print('Calling', args, kwargs)

>>> c = Callable()
>>> c(2, 3, color='red')
Calling (2, 3) {'color': 'red'}
>>>

The deconstruct method seems to provide a point where the client (i.e. you) can override serializing behavior by writing custom implementations. For example, see here. This seems similar to the clean method, where you can implement custom input sanitation for your models and gets called automatically when full_clean is invoked (e.g. when a form uses is_valid).

The __eq__ allows you to implement a comparison between 2 objects that are themselves not comparable. For example, if you have a

class Vector2:
    def __init__(self, x, y):
        self.x = x
        self.y = y

your __eq__ implementation could look like this to check for equality between two vector objects:

# ...
def __eq__(self, other):
    return self.x == other.x and self.y == other.y

This way, you avoid a shallow comparison of the underlying references.

1
votes

Starts from the beginning, there are almost none cons - except of maybe some complexity in implementing class based validators.

But there are some pros: yon can save in class instance something for future validation, so it won't be computed each time something is validated, for example compiled regex pattern. You can also create more complex validator by spreading code into other methods in your class.

Also, you can construct your validator with some parameters which can be used later in validation process.

__call__ method is actual validation function - it will be called like normal validation function with same parameters (and additional self parameter - instance of class, like in all methods). And it's not something from django framework, it's from python itself. Any class can be called, like a function, if it has __call__ method implemented. deconstruct method is explained in migration serializing. __eq__ is also from python itself, each class can have that and it will simply compare 2 objects to check if they are equal.