Django

Code

Ticket #3288 (closed: fixed)

Opened 1 year ago

Last modified 3 weeks ago

ForeignKey to 'self' and select_related() = infinite recursion

Reported by: Max Derkachev <mderk@yandex.ru> Assigned to: PhiR
Component: Database wrapper Version: SVN
Keywords: qs-rf-fixed Cc:
Triage Stage: Accepted Has patch: 1
Needs documentation: 0 Needs tests: 0
Patch needs improvement: 1

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

3288.diff (3.4 kB) - added by PhiR on 09/16/07 09:21:46.
fix and tests
3288_96.diff (3.5 kB) - added by PhiR on 09/16/07 09:22:20.
fix and tests, backported to 0.96

Change History

01/20/07 17:41:18 changed by mir@noris.de

  • stage changed from Unreviewed to Accepted.

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.

01/24/07 09:33:07 changed by mir@noris.de

this is a special case of bug #2885, which I've marked as duplicate of this one.

09/15/07 20:07:44 changed by PhiR

  • keywords set to qs-rf.
  • owner changed from nobody to PhiR.
  • has_patch set to 1.
  • version set to SVN.
  • status changed from new to assigned.

09/15/07 20:22:15 changed by PhiR

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.

09/16/07 08:54:32 changed by PhiR

special case of #2549.

09/16/07 09:21:46 changed by PhiR

  • attachment 3288.diff added.

fix and tests

09/16/07 09:22:20 changed by PhiR

  • attachment 3288_96.diff added.

fix and tests, backported to 0.96

09/16/07 11:05:10 changed by PhiR

  • stage changed from Accepted to 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.

09/30/07 08:16:55 changed by pkenjora

  • needs_better_patch set to 1.

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

09/30/07 08:28:13 changed by pkenjora

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.

10/15/07 00:51:20 changed by mtredinnick

(In [6521]) queryset-refactor: Fixed a possibility of shooting oneself in the foot and creating infinite recursion with select_related(). Refs #3045, #3288.

10/15/07 01:02:30 changed by mtredinnick

  • keywords changed from qs-rf to qs-rf-fixed.
  • stage changed from Ready for checkin to Accepted.

12/18/07 18:01:48 changed by alfred.einstein@gmail.com

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

01/26/08 10:34:07 changed by ramiro

Marked #6250 as duplicate

02/28/08 14:05:02 changed by ljernejcic@whatido.us

I wanted to note that this error popups for OneToOneFields?: location_category = models.OneToOneField?('self', blank=True)

04/26/08 21:50:16 changed by mtredinnick

  • status changed from assigned to closed.
  • resolution set to fixed.

(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


Add/Change #3288 (ForeignKey to 'self' and select_related() = infinite recursion)




Change Properties
Action