Slight modification to default `__repr__` for models?

I was looking into an old issue about making DoesNotExist errors nicer (Fixed #20278 -- ensured .get() exceptions do not recurse infinitely · django/django@266c0bb · GitHub) which was reverted due to an infinite recursion, and I realized that the reason this problem occurs is because of another issue that I always patch in my projects: the default __repr__ on models calls __str__.

Current implementation:

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self)

In all my projects I always have a common base class for my models that contain:

    def __repr__(self):
        return f'<{self.__class__.__name__} pk={self.pk}>'

With this in place, the repr of an object won’t infinitely recurse by default, which means we can make DoesNotExist nicer.

A side not on this is that it’s also the case that the fix for DoesNotExist being reverted was because the error can end up in an infinite loop. But that’s also true for MultipleObjectsReturned. So logically we should either make DoesNotExist work or make MultipleObjectsReturned worse. I prefer the first :stuck_out_tongue:

3 Likes

I’ve thought about this some more. I think this change is good, but I ALSO think the DoesNotExist and MultipleObjectsReturned errors should NOT use repr(obj), but instead use Model.__repr__(obj) which (after this change) would guarantee that the error string can never recurse infinitely.

Past proposals to change Model.__repr__() have been rejected.

https://code.djangoproject.com/ticket/20448
https://groups.google.com/g/django-developers/c/UNKFMg6DO5s/m/3nZECHyRAgAJ

https://code.djangoproject.com/ticket/28839
https://groups.google.com/g/django-developers/c/7Jqzwg5nt-c/m/oYxx9CgBBQAJ

https://groups.google.com/g/django-developers/c/4yd8wBD3BiY/m/139xyLaEBAAJ

Well… sort of.

  1. super old, and really talks about a different situation before the pk was included
  2. closed as already fixed, so not really relevant
  3. Adam points out that this is already the default if you don’t override __str__, which is my point. If you DO override it you will potentially shoot yourself in the foot very badly. (Fun fact, it was me who Fabian talks about on the mailing list, it was my suggestion to him :P)