Подтвердить что ты не робот

Django сравнивает экземпляры модели для равенства

Я понимаю, что в ситуации singleton вы можете выполнить такую ​​операцию, как:

spam == eggs

и если spam и eggs являются экземплярами одного и того же класса со всеми одинаковыми значениями атрибутов, он возвращает True. В модели Django это естественно, потому что два отдельных экземпляра модели никогда не будут одинаковыми, если они не имеют одинаковое значение .pk.

Проблема заключается в том, что если ссылка на экземпляр имеет атрибуты, которые были обновлены промежуточным программным обеспечением где-то на этом пути, и оно не было сохранено, и вы пытаетесь выполнить его с другой переменной, содержащей ссылку на экземпляр той же модели, он вернет False, конечно, потому что у них разные значения для некоторых атрибутов. Очевидно, мне не нужно что-то вроде singleton, но мне интересно, есть ли какой-нибудь официальный метод Djangonic (ha, новое слово) для проверки этого, или если я должен просто проверить, что значение .pk совпадает с:

spam.pk == eggs.pk

Извините, если это была огромная трата времени, но похоже, что может быть метод для этого, и что-то мне не хватает, что я буду сожалеть по дороге, если не найду он.

ОБНОВЛЕНИЕ (02-27-2015)

Вы должны игнорировать первую часть этого вопроса, так как вам не следует сравнивать одиночные числа с ==, а скорее с is. Синглтоны действительно не имеют никакого отношения к этому вопросу.

4b9b3361

Ответ 1

Из документация django:

Чтобы сравнить два экземпляра модели, просто используйте стандартный оператор сравнения Python, знак double равно: ==. За кулисами, которая сравнивает значения первичных ключей двух моделей.

Ответ 2

spam.pk == eggs.pk - хороший способ сделать это.

Вы можете добавить __eq__ к своей модели, но я этого избегу, потому что это запутывает, поскольку == может означать разные вещи в разных контекстах, например. Я хочу, чтобы == означал, что контент такой же, id может отличаться, поэтому лучший способ -

spam.pk == eggs.pk

Изменить: btw in django 1.0.2 Класс модели определил __eq__ как

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

который кажется таким же, как spam.pk == eggs.pk as pk является свойством, которое использует _get_pk_val поэтому я не понимаю, почему spam == eggs не работает?

Ответ 3

Начиная с Django 2.2.1, исходный код для равенства экземпляров модели выглядит так:

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

То есть два экземпляра модели равны, если они взяты из одной и той же таблицы базы данных и имеют один и тот же первичный ключ. Если один из первичных ключей равен None они равны, только если они являются одним и тем же объектом.

(Итак, возвращаясь к вопросу OP, просто сравнив экземпляры, было бы хорошо.)

Ответ 5

Только для записи, сравнивая:

    spam == eggs

является опасным, если есть вероятность, что любой из них может быть отложенным экземпляром модели, созданным запросом Model.objects.raw() или .defer(), примененным к "нормальному" QuerySet.

Здесь я расскажу подробнее: Проблема Django QuerySet.defer() - ошибка или функция?

Ответ 6

Как отмечает орокусаки, "если ни один экземпляр не имеет первичного ключа, он всегда будет возвращать true". Если вы хотите, чтобы это работало, вы могли бы расширить свою модель следующим образом:

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

Ответ 7

Было бы странно, если бы два экземпляра модели сравнивались как равные, если у них были разные атрибуты. В большинстве случаев это было бы нежелательно.

Что вы хотите - это особый случай. Сравнение spam.pk == eggs.pk - хорошая идея. Если там еще нет pk, потому что они не были сохранены, тогда сложнее определить, какие экземпляры "действительно" одинаковы, если некоторые атрибуты разные.

Как добавить пользовательский атрибут к вашим экземплярам при их создании, например: spam.myid=1, eggs.myid=2

Таким образом, в какой-то момент вашего кода, когда spamcopy1.seasoning=ketchup и spamcopy2.seasoning=blackpepper вы можете сравнить свой атрибут myid, чтобы увидеть, действительно ли он является тем же самым спамом.