Ответ 1
Чтобы сравнить два экземпляра модели, просто используйте стандартный оператор сравнения Python, знак double равно: ==. За кулисами, которая сравнивает значения первичных ключей двух моделей.
Я понимаю, что в ситуации singleton вы можете выполнить такую операцию, как:
spam == eggs
и если spam
и eggs
являются экземплярами одного и того же класса со всеми одинаковыми значениями атрибутов, он возвращает True
. В модели Django это естественно, потому что два отдельных экземпляра модели никогда не будут одинаковыми, если они не имеют одинаковое значение .pk
.
Проблема заключается в том, что если ссылка на экземпляр имеет атрибуты, которые были обновлены промежуточным программным обеспечением где-то на этом пути, и оно не было сохранено, и вы пытаетесь выполнить его с другой переменной, содержащей ссылку на экземпляр той же модели, он вернет False
, конечно, потому что у них разные значения для некоторых атрибутов. Очевидно, мне не нужно что-то вроде singleton, но мне интересно, есть ли какой-нибудь официальный метод Djangonic (ha, новое слово) для проверки этого, или если я должен просто проверить, что значение .pk
совпадает с:
spam.pk == eggs.pk
Извините, если это была огромная трата времени, но похоже, что может быть метод для этого, и что-то мне не хватает, что я буду сожалеть по дороге, если не найду он.
Вы должны игнорировать первую часть этого вопроса, так как вам не следует сравнивать одиночные числа с ==
, а скорее с is
. Синглтоны действительно не имеют никакого отношения к этому вопросу.
Чтобы сравнить два экземпляра модели, просто используйте стандартный оператор сравнения Python, знак double равно: ==. За кулисами, которая сравнивает значения первичных ключей двух моделей.
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
не работает?
Начиная с 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, просто сравнив экземпляры, было бы хорошо.)
Вы можете определить метод Class '__eq__
, чтобы изменить это поведение:
Только для записи, сравнивая:
spam == eggs
является опасным, если есть вероятность, что любой из них может быть отложенным экземпляром модели, созданным запросом Model.objects.raw() или .defer(), примененным к "нормальному" QuerySet.
Здесь я расскажу подробнее: Проблема Django QuerySet.defer() - ошибка или функция?
Как отмечает орокусаки, "если ни один экземпляр не имеет первичного ключа, он всегда будет возвращать 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
Было бы странно, если бы два экземпляра модели сравнивались как равные, если у них были разные атрибуты. В большинстве случаев это было бы нежелательно.
Что вы хотите - это особый случай. Сравнение spam.pk == eggs.pk
- хорошая идея. Если там еще нет pk
, потому что они не были сохранены, тогда сложнее определить, какие экземпляры "действительно" одинаковы, если некоторые атрибуты разные.
Как добавить пользовательский атрибут к вашим экземплярам при их создании, например:
spam.myid=1
, eggs.myid=2
Таким образом, в какой-то момент вашего кода, когда spamcopy1.seasoning=ketchup
и spamcopy2.seasoning=blackpepper
вы можете сравнить свой атрибут myid
, чтобы увидеть, действительно ли он является тем же самым спамом.