#12876 closed (fixed)
maximum recursion error when caching querysets on models
| Reported by: | zbyte64 | Owned by: | elachuni |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 1.2-beta |
| Severity: | Keywords: | pycamp2010 | |
| Cc: | zbyte64@… | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
The following test case causes a runtime exception against django 1.2 beta 1:
from django.test import TestCase
from django.db import models
class TestModel(models.Model):
def cachedmethod(self):
if not hasattr(self, '_cachedmethod'):
self._cachedmethod = RelatedModel.objects.all().filter(test_model=self)
return self._cachedmethod
class RelatedModel(models.Model):
test_model = models.ForeignKey(TestModel)
category = models.CharField(max_length=50)
class dummy(object):
def __init__(self, parent):
self.parent = parent
def cachedmethod(self):
if not hasattr(self, '_cachedmethod'):
self._cachedmethod = RelatedModel.objects.all().filter(test_model=self.parent)
return self._cachedmethod
class BreakMeTest(TestCase):
def test_caching(self):
parent = TestModel()
parent.save()
related = RelatedModel(category='foo', test_model=parent)
related.save()
adummy = dummy(parent)
adummy.cachedmethod()
adummy.cachedmethod().filter(category='foo') #this doesn't break it
parent.cachedmethod()
parent.cachedmethod().filter(category='foo') #this breaks it
Attachments (2)
Change History (11)
by , 16 years ago
| Attachment: | results.txt added |
|---|
comment:1 by , 16 years ago
| milestone: | → 1.2 |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 16 years ago
| Component: | Uncategorized → Database layer (models, ORM) |
|---|
follow-up: 5 comment:3 by , 16 years ago
| Owner: | changed from to |
|---|
django.db.models.sql.Query defines a custom __deepcopy__ that calls self.clone(), disregarding the provided memo dict.
So, if the query contains a reference to a model instance that in turn contains a reference to a reference (as in this case), deepcopy will end up in an endless loop.
The model itself could work around this problem by providing its own __deepcopy__ implementation, that just didn't include the query. For the example in the bugreport:
class TestModel(models.Model): def __deepcopy__(self, memo): return TestModel(id=self.id) ....
comment:4 by , 16 years ago
| Has patch: | set |
|---|---|
| Keywords: | pycamp2010 added |
comment:5 by , 16 years ago
The attached patch adds an optional argument 'memo' to clone, to be able to correctly implement deepcopy.
Replying to elachuni:
So, if the query contains a reference to a model instance that in turn contains a reference to a reference (...)
Should have been a reference to a query here.
comment:6 by , 16 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
comment:7 by , 16 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
Stack trace of exception