| 497 | return super().__eq__(other) # pragma no cover |
| 498 | |
| 499 | def __hash__(self) -> int: |
| 500 | cached = getattr(self, "__cached_hash__", None) |
| 501 | if cached is not None: |
| 502 | return cached |
| 503 | |
| 504 | pk = self.pk |
| 505 | cls = type(self) |
| 506 | if pk is not None: |
| 507 | # ``type(self)`` hashes by identity in CPython, so ``hash((pk, cls))`` |
| 508 | # is uniqueness-equivalent to the original ``str(pk) + cls.__name__`` |
| 509 | # without two string allocations per call. This is the hot path — |
| 510 | # everything that goes through ``_relation_cache`` is keyed on |
| 511 | # saved-pk Models. |
| 512 | ret = hash((pk, cls)) |
| 513 | else: |
| 514 | # Unsaved models can hold list/dict values in ``__dict__`` (json |
| 515 | # fields, reverse-relation slots), so we still ``str(vals)`` to |
| 516 | # keep the result hashable. Cold path; not perf-critical. |
| 517 | related = self.extract_related_names() |
| 518 | vals = {k: v for k, v in self.__dict__.items() if k not in related} |
| 519 | ret = hash((str(vals), cls)) |
| 520 | |
| 521 | object.__setattr__(self, "__cached_hash__", ret) |
| 522 | return ret |
| 523 | |
| 524 | def __same__(self, other: "NewBaseModel") -> bool: |
| 525 | """ |