#19362: Recursion error when deleting model object through admin
-------------------------------+---------------------------------------
Reporter: m3wolf | Owner: nobody
Type: Bug | Status: new
Component: Uncategorized | Version: 1.5-alpha-1
Severity: Normal | Resolution:
Keywords: python2.7.3 | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------+---------------------------------------
Comment (by aaugustin):
Here's what happens when a subclass of `models.Model` that doesn't define
`__str__` is decorated with `python_2_unicode_compatible`:
- when the decorator is executed:
- its `__unicode__` method points to the `__str__` method from
`models.Model` — which returns a `str` and not a `unicode` as expected by
`python_2_unicode_compatible`
- its `__str__` method is a new method that calls `__unicode__` and
returns the result encoded as utf-8
- when attempting to create the unicode representation of an instance,
say, `obj`:
- the interpreter treats `unicode(obj)` as `type(obj).__unicode__(obj)`
- in this case, that's actually `models.Model.__str__(obj)`
- this function calls `force_text(obj)`, which calls`obj.__unicode__()`
— and that's also `models.Model.__str__(obj)`, hence the infinite
recursion.
----
I wish I could just remove the definition of `models.Model.__str__`, but
after having spent years educating developers to write a `__unicode__`
method for their models, this isn't a viable option.
It's possible to catch this programming mistake and raise an explicit
exception at runtime like this:
{{{
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -416,6 +416,11 @@ class Model(six.with_metaclass(ModelBase, object)):
def __str__(self):
if not six.PY3 and hasattr(self, '__unicode__'):
+ if type(self).__unicode__ == Model.__str__:
+ klass_name = type(self).__name__
+ raise RuntimeError("%s.__unicode__ is aliased to __str__.
Did"
+ " you apply
@python_2_unicode_compatible"
+ " without defining __str__?" %
klass_name)
return force_text(self).encode('utf-8')
return '%s object' % self.__class__.__name__
}}}
That ugly, but it works... It's more difficult to catch the exception at
compile time because `python_2_unicode_compatible` is defined in
`django.utils.encoding`, and I don't want to import
`django.db.models.Model` there.
----
m3wolf, I have an explanation for why you can delete one instance and not
another of the same model.
You probably have a foreign key pointing from an instance of another model
to the instance you can't delete, and that other model has
`@python_2_unicode_compatible` but no `__str__` method. When Django tries
to display that instance to warn you about the cascade deletion, it enters
the infinite loop.
If you apply the patch above, Django should tell you which model has this
issue. Could you test that and report your results here?
--
Ticket URL: <https://code.djangoproject.com/ticket/19362#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit https://groups.google.com/groups/opt_out.