Aliquis
Aliquis

Reputation: 2319

Django: When do I need __eq__ method?

In Django I've written a custom class-based validator and implemented the __eq__ method because it's also done for the validators that are already implemented by Django itself.

Here you can see it:

@deconstructible
class FileSizeValidator:

    def __init__(self, mb):
        self.mb = mb

    def __call__(self, value):
        limit = self.mb * 1024 * 1024
        if value.size > limit:
            raise ValidationError('File too big (max. ' + str(self.mb) + ' MiB).')

    def __eq__(self, other):
        return (
            isinstance(other, FileSizeValidator) and
            self.mb == other.mb
        )

Now, I've also written a custom class for the ImageField. I want to use it for the upload_to= parameter in a model for the ImageField. Here you can see my example:

@deconstructible
class RandomFileName:

    def __init__(self, directory=''):
        self.directory = directory

    def __call__(self, instance, filename):
        return self.directory + ("/" if self.directory else '') + get_random_string(7) + "." + filename.split('.')[-1]

    def __eq__(self, other):
        return (
            isinstance(other, RandomFileName) and
            self.directory == other.directory
        )

In general, I am happy with it and it seems to work fine. I just do not know when I need the __eq__ method and would like to ask you for an explanation.

Upvotes: 4

Views: 2578

Answers (2)

Showkat Ali
Showkat Ali

Reputation: 23

I think it is no longer relevant to your matter of concern. But, your question is interesting and the existing answer is not clear enough, so I decided dive into the concept. The outcome is as follow:

Your question: Django: When do I need __eq__ method?

The answer is: Whenever you want to break the default rule of python to compare two custom objects by redefining your own rule, then you need this strategy.

Remember, the __eq__ method is not just tied to Django Validator class only. It is rather applicable in any python Class you create.

Now lets dive into details. Suppose you have a class named MyClass as follow:

class MyClass:
    def __init__(self, age):
        self.age = age
    

Now You initialize three different objects of this class:

object1 = MyClass(20)
object2 = MyClass(20)
object3 = MyClass(30)

Now if I asked you, are object1 and object2 the same? What is your answer? Yes, right?

Yes, you are incorrect. Didn't believe me? Then go your terminal and try by yourself. These are not same to python Interpreter because, The Interpreter compare your class objects by the object's memory address. Since the memory address of object1 and object1 are different, it treats as unequal.

But we want to break this rule. We want to educate python Interpreter to compare my objects not based on their memory address, instead by my instructions. Lets redefine the class again.

class MyClass:
    def __init__(self, age):
        self.age = age

    def __eq__(self, other):
        return isinstance(other, MyClass) and self.age == other.age

and now initialize three objects of the class again,

object1 = MyClass(20)
object2 = MyClass(20)
object3 = MyClass(30)

and compare object1 to object2 in terminal

object1 == object2:

This time it will return True. Because, now the Interpreter is no longer following its own rule to perform the == comparison operator for custom objects. Instead, it has found in the class's __eq__ method that the comparison result will be True if the second object's class is same as first objects class isinstance(other, MyClass) and the age of second object is same as the age of first object self.age == other.age

So, why do I need to include the __eq__ method in my Custom Validator Classes?

This is because, when you defined a custom validation class and performed ./manage.py makemigrations then the migration framework of django created object of your Custom Validation class, for your case, it is FileSizeValidator class. and when for the next times you perform makemigrations command, it then compares to check if you have updated your validation requirements (for your case, file size). and if the system finds that the existing object and new object are not equal, then it then create new migration object for your updated validation requirements.

So, Is it required to have this method in my validation classes?

Not at all. But, it is totally up to your requirements. if you think, you never need to update validation criteria, so no need. But if you think you might need to update it, for example, currently you are allowing user to upload file of maximum size 5mb, and sooner or later you might need to change it to either less or more, then you must redefine the __eq__ method. Because __eq__ is the way to communicate the system about your validation classes criteria changes.

Upvotes: 0

Ankit Jaiswal
Ankit Jaiswal

Reputation: 23437

This is called operator overloading and in your example, it's for comparing two objects. After implementing __eq__() method, you can compare any other object for equality with your object.

We can now create two objects from your class:

obj1 = FileSizeValidator(100)
obj2 = FileSizeValidator(200)

and can compare them as obj1 == obj2.

Since we have custom __eq__() defined, this would compare the class of the object and the value of mb attribute to return a True or False value. If any of the condition fails, it will return False.

By default in python an equality operator for two custom objects compares the id values of the objects and would never return True except when comparing an object with itself.

Upvotes: 2

Related Questions