Opened 19 years ago
Closed 18 years ago
#3288 closed defect (fixed)
ForeignKey to 'self' and select_related() = infinite recursion
| Reported by: | Owned by: | Philippe Raoult | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev | 
| Severity: | major | Keywords: | qs-rf-fixed | 
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | yes | 
| Easy pickings: | no | UI/UX: | no | 
Description
Maybe it's a variant of #3045 bug.
The [simplified] case is
class Foo(models.Model): parent = models.ForeignKey('self') class Meta: db_table = 'foos' foo = Foo.objects.select_related().get(pk=1)
That leads to infinite recursion in django.db.models.query.fill_table_cache. The bottom of the stack trace is:
  ........ many many lines ............
  File "/home/maxim/dproject/django/db/models/query.py", line 684, in fill_table_cache
    fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen)
  File "/home/maxim/dproject/django/db/models/query.py", line 684, in fill_table_cache
    fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen)
  File "/home/maxim/dproject/django/db/models/query.py", line 681, in fill_table_cache
    where.append('%s.%s = %s.%s' % \
  File "/home/maxim/dproject/django/db/models/fields/related.py", line 737, in get_related_field
    return self.to._meta.get_field(self.field_name)
  File "/home/maxim/dproject/django/db/models/options.py", line 98, in get_field
    if f.name == name:
RuntimeError: maximum recursion depth exceeded in cmp
      Attachments (2)
Change History (16)
comment:1 by , 19 years ago
| Triage Stage: | Unreviewed → Accepted | 
|---|
comment:2 by , 19 years ago
this is a special case of bug #2885, which I've marked as duplicate of this one.
comment:3 by , 18 years ago
| Has patch: | set | 
|---|---|
| Keywords: | qs-rf added | 
| Owner: | changed from to | 
| Status: | new → assigned | 
| Version: | → SVN | 
comment:4 by , 18 years ago
although this is marked qs-rf, it is a very simple patch and it can also be applied to 96! The regression test should go in the trunk so qs-rf will be tested against it whenever it is merged. 
comment:6 by , 18 years ago
| Triage Stage: | Accepted → Ready for checkin | 
|---|
It should be noted that the attached patches also fix #2549 and #3045 at the same time. The regression tests cover both their cases (FK cycle) and ours (FK to self). I'm adding patches even though this is marked qs-rf because this is a nasty bug and 0.96 users might want to have it. Also we might as well put the reg tests in trunk so this issue isn't replicated by qs-rf. 
comment:7 by , 18 years ago
| Patch needs improvement: | set | 
|---|
I applied 3288_96.diff with no effect.  My model is:
class ProjectImage(models.Model):
project = models.ForeignKey("Project", null=True, edit_inline=True, core=True)
title = models.CharField(maxlength=50, blank=True)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='project', height_field="100", width_field="100", blank=False)
faqs = models.ManyToManyField(Faq, blank=True)
categories = models.ManyToManyField(Category)
class Project(models.Model):
title = models.CharField(maxlength=50)
description = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
icon = models.ForeignKey(ProjectImage, edit_inline=True, core=True, related_name='icon')
parts = models.ManyToManyField(Part)
cars = models.ManyToManyField(Car)
faqs = models.ManyToManyField(Faq, blank=True)
The patch does not break any functionality but I still encounter the same error.
Exception Type: RuntimeError 
Exception Value: maximum recursion depth exceeded in cmp 
Exception Location: /home/patrickm/lib/python2.4/django/db/models/fields/init.py in get_follow, line 328 
comment:8 by , 18 years ago
Adding edit_inline=True seems to break the patch.
In above example make error go away by changing:
project = models.ForeignKey("Project", null=True, edit_inline=True, core=True) 
to:
project = models.ForeignKey("Project", null=True, core=True)
That resolved the issue if it persists after patch is applied.
comment:9 by , 18 years ago
comment:10 by , 18 years ago
| Keywords: | qs-rf-fixed added; qs-rf removed | 
|---|---|
| Triage Stage: | Ready for checkin → Accepted | 
comment:11 by , 18 years ago
Dunno if the same bug but I got something similar with this:
class Test(models.Model):
	recursion_loop = models.ForeignKey('self',edit_inline=models.TABULAR)
this is the output
  File "/home/visi/djtrunk/django/db/models/related.py", line 99, in get_follow
    return self.opts.get_follow(over)
  File "/home/visi/djtrunk/django/db/models/options.py", line 177, in get_follow
    fol = f.get_follow(child_override)
  File "/home/visi/djtrunk/django/db/models/related.py", line 99, in get_follow
    return self.opts.get_follow(over)
  File "/home/visi/djtrunk/django/db/models/options.py", line 177, in get_follow
    fol = f.get_follow(child_override)
  File "/home/visi/djtrunk/django/db/models/related.py", line 99, in get_follow
    return self.opts.get_follow(over)
  File "/home/visi/djtrunk/django/db/models/options.py", line 177, in get_follow
    fol = f.get_follow(child_override)
  File "/home/visi/djtrunk/django/db/models/related.py", line 99, in get_follow
    return self.opts.get_follow(over)
  File "/home/visi/djtrunk/django/db/models/options.py", line 177, in get_follow
    fol = f.get_follow(child_override)
  File "/home/visi/djtrunk/django/db/models/fields/__init__.py", line 372, in get_follow
    if override != None:
RuntimeError: maximum recursion depth exceeded in cmp
comment:13 by , 18 years ago
I wanted to note that this error popups for OneToOneFields: location_category = models.OneToOneField('self', blank=True)
comment:14 by , 18 years ago
| Resolution: | → fixed | 
|---|---|
| Status: | assigned → closed | 
(In [7477]) Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
I could reproduce the bug. The db_table setting is not necessary. Funny, it really works (as reported) without any data in the table for foo.
I'm closing #3045 in favour of this ticket since it appears to be the same bug, and #3288 is a lot clearer. But if we have a patch for #3288, please test it also for #3045! The conditions for #3045 are a bit different and more complicated.