Opened 11 years ago
Closed 10 years ago
#23739 closed Cleanup/optimization (invalid)
django 1.7.1 defer() throws AttributeError when using related_name
| Reported by: | Farhan Ahammed | Owned by: | nobody |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 1.7 |
| Severity: | Normal | Keywords: | defer related_name AttributeError RelatedObject rel |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
I have attached a simple example project, with the following Models defined:
class ModelA(models.Model): name = models.CharField(max_length=255) class ModelB(models.Model): name = models.CharField(max_length=255) model_a = models.ForeignKey(ModelA, related_name='model_b')
Entering the following code (in a Django shell) works fine:
ModelB.objects.filter(name='').select_related('model_a').defer('model_a__name')
But when I run the following:
ModelA.objects.filter(name='').select_related('model_b').defer('model_b__name')
I get this error message:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 116, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 141, in __iter__
self._fetch_all()
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 966, in _fetch_all
self._result_cache = list(self.iterator())
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 222, in iterator
only_load = self.query.get_loaded_field_names()
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1821, in get_loaded_field_names
self.deferred_to_data(collection, self.get_loaded_field_names_cb)
File "/home/fahammed/Documents/django_defer_bug/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 596, in deferred_to_data
cur_model = source.rel.to
AttributeError: 'RelatedObject' object has no attribute 'rel'
Looks like defer doesn't work when you specify the related table using its related_name.
Attachments (2)
Change History (9)
by , 11 years ago
| Attachment: | django_defer_bug.tar.gz added |
|---|
comment:1 by , 11 years ago
| Component: | Uncategorized → Database layer (models, ORM) |
|---|---|
| Type: | Uncategorized → Bug |
comment:2 by , 11 years ago
| Severity: | Normal → Release blocker |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
This seems to be a regression present since 1.6 -- bisected to 70679243d1786e03557c28929f9762a119e3ac14. Test for Django's test suite attached.
by , 11 years ago
| Attachment: | 23739-test.diff added |
|---|
comment:3 by , 11 years ago
| Severity: | Release blocker → Normal |
|---|---|
| Type: | Bug → Cleanup/optimization |
The reason pre-1.6 didn't raise exceptions was that the chunked reads implementation swallowed exceptions. I don't think there is a regression here, we just get an exception instead of silently returning empty list on error.
I tested this on Django 1.5 with the following command: [o for o in ModelB.objects.filter(name='').select_related('model_b').defer('model_b__name').iterator()]. This raises a different error (ModelB has no field named 'model_b'), but doesn't produce any results.
We might want to allow usage of related_name in this context, and even if we don't, users shouldn't get such an ugly error message.
comment:4 by , 11 years ago
@akaariai That's not the code that was causing the error message. From ModelB.objects.filter(name=''), you select the related objects from ModelA and then defer the ModelA.name value from the DB. So your command should probably be:
[o for o in ModelB.objects.filter(name='').select_related('model_a').defer('model_a__name').iterator()]
Note that ModelB has no field called model_b} but it does have a field called model_a.
comment:5 by , 11 years ago
I'm confused. Using [o for o in ModelA.objects.filter(name='').select_related('model_b').defer('model_b__name')] gives me RelatedObject has no attribute rel in 1.5. Just as it does using master. And, [o for o in ModelB.objects.filter(name='').select_related('model_a').defer('model_a__name').iterator()] works on master.
comment:6 by , 11 years ago
I think that's because the attribute model_a has been explicitly defined in the class definition of ModelB, whereas the class definition of ModelA does not have an attribute called model_b. Correct me if I'm wrong, but the ModelA.model_b is dynamically created at runtime (or loaded when the value is called)?
On another (possibly) similar note, calling the only() function:
ModelA.objects.filter(name='').select_related('model_b').only('model_b__name')
also returns the same error AttributeError: 'RelatedObject' object has no attribute 'rel', and the following line works fine:
ModelB.objects.filter(name='').select_related('model_a').only('model_a__name')
comment:7 by , 10 years ago
| Resolution: | → invalid |
|---|---|
| Status: | new → closed |
The problem seems to be an invalid select_related() query. On Django 1.9:
>>> ModelA.objects.filter(name='').select_related('model_b').defer('model_b__name')
django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'model_b'. Choices are: (none)
You should be using prefech_related() when querying reverse foreign keys.
Example project to run/test the bug