#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.


Reply via email to