Django comparing model instances for equality

后端 未结 8 1649
夕颜
夕颜 2020-12-01 11:53

I understand that, with a singleton situation, you can perform such an operation as:

spam == eggs

and if spam and eggs

相关标签:
8条回答
  • 2020-12-01 12:16

    From Django documentation:

    To compare two model instances, just use the standard Python comparison operator, the double equals sign: ==. Behind the scenes, that compares the primary key values of two models.

    0 讨论(0)
  • 2020-12-01 12:17

    spam.pk == eggs.pk is a good way to do that.

    You may add __eq__ to your model but I will avoid that, because it is confusing as == can mean different things in different contexts, e.g. I may want == to mean content is same, id may differ, so again best way is

    spam.pk == eggs.pk
    

    Edit: btw in django 1.0.2 Model class has defined __eq__ as

    def __eq__(self, other):
        return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() 
    

    which seems to be same as spam.pk == eggs.pk as pk is property which uses _get_pk_val so I don't see why spam == eggs is not working ?

    0 讨论(0)
  • 2020-12-01 12:17

    As orokusaki comments, "if neither instance has a primary key, it will return true always". If you want this to work, you could extend your model like so:

    def __eq__(self, other):
        eq = isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()
    
        if eq and self._get_pk_val() is None:
            return id(self) == id(other)
        return eq
    
    0 讨论(0)
  • 2020-12-01 12:21

    Just for the record, comparing:

        spam == eggs
    

    is dangerous if there is any chance that either of them could be a deferred model instance created by Model.objects.raw() query or by .defer() applied to a 'normal' QuerySet.

    I put more details here: Django QuerySet .defer() problem - bug or feature?

    0 讨论(0)
  • 2020-12-01 12:25

    As of Django 3.0.2, the source code for model instance equality is this:

    def __eq__(self, other):
        if not isinstance(other, Model):
            return False
        if self._meta.concrete_model != other._meta.concrete_model:
            return False
        my_pk = self.pk
        if my_pk is None:
            return self is other
        return my_pk == other.pk
    

    That is, two model instances are equal if they come from the same database table and have the same primary key. If either primary key is None they're only equal if they're the same object.

    (So getting back to the OP's question, simply comparing the instances would be fine.)

    0 讨论(0)
  • 2020-12-01 12:27

    It would be strange if two model instances compared as equal if they had different attributes. Most of the time that would be undesirable.

    What you want is a special case. Comparing spam.pk == eggs.pk is a good idea. If there's no pk yet, because they haven't been saved, then it's harder to define which instances are "really" the same if some attributes are different.

    How about adding a custom attribute to your instances when creating them, eg: spam.myid=1, eggs.myid=2

    That way at some point in your code when spamcopy1.seasoning=ketchup and spamcopy2.seasoning=blackpepper you can compare their myid attribute to see if they're really the "same" spam.

    0 讨论(0)
提交回复
热议问题