#26551 closed Bug (fixed)
Django ORM generates invalid PostgreSQL query with Inverted Q() object that crosses relations
Reported by: | Erik B. Andersen | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | Q(), postgreSQL |
Cc: | mail@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I found that the following code creates an invalid PostgreSQL query:
AgentAgreement.objects.filter(~(Q(book__listings__contract__handoffdate__lte=timezone.now()) & Q(book__listings__contract__returndate=None) ))
But, this does not
BookInstance.objects.filter(~(Q(listings__contract__handoffdate__lte=timezone.now()) & Q(listings__contract__returndate=None) ))
The error I get is:
Traceback (most recent call last): File "<console>", line 1, in <module> File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/models/query.py", line 234, in __repr__ data = list(self[:REPR_OUTPUT_SIZE + 1]) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/models/query.py", line 258, in __iter__ self._fetch_all() File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/models/query.py", line 1074, in _fetch_all self._result_cache = list(self.iterator()) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/models/query.py", line 52, in __iter__ results = compiler.execute_sql() File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 848, in execute_sql cursor.execute(sql, params) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute return super(CursorDebugWrapper, self).execute(sql, params) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute return self.cursor.execute(sql, params) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/utils.py", line 95, in __exit__ six.reraise(dj_exc_type, dj_exc_value, traceback) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/utils/six.py", line 685, in reraise raise value.with_traceback(tb) File "/tmp/test2/testenv/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute return self.cursor.execute(sql, params) django.db.utils.ProgrammingError: invalid reference to FROM-clause entry for table "problemdemo_bookinstance" LINE 1: ...d") WHERE (U3."returndate" IS NULL AND U1."id" = ("problemde... ^ HINT: Perhaps you meant to reference the table alias "u1".
The SQL generated, (with whitespace added by me) is:
SELECT "problemdemo_agentagreement"."id", "problemdemo_agentagreement"."book_id" FROM "problemdemo_agentagreement" WHERE NOT ( "problemdemo_agentagreement"."book_id" IN ( SELECT U2."book_instance_id" AS Col1 FROM "problemdemo_listing" U2 INNER JOIN "problemdemo_contract" U3 ON (U2."id" = U3."listing_id") WHERE U3."handoffdate" <= 2016-04-27 19:11:05.130778+00:00 ) AND "problemdemo_agentagreement"."book_id" IN ( SELECT U1."id" AS Col1 FROM "problemdemo_bookinstance" U1 LEFT OUTER JOIN "problemdemo_listing" U2 ON (U1."id" = U2."book_instance_id") LEFT OUTER JOIN "problemdemo_contract" U3 ON (U2."id" = U3."listing_id") WHERE ( U3."returndate" IS NULL AND U1."id" = ("problemdemo_bookinstance"."id") ) ) )
This is using the following models.py:
from django.db import models # from django.utils import timezone # from django.db.models import Q # from problemdemo.models import * # AgentAgreement.objects.filter(~(Q(book__listings__contract__handoffdate__lte=timezone.now()) & Q(book__listings__contract__returndate=None) )) class BookInstance(models.Model): someattr = models.CharField(max_length=13, blank=True) class Contract(models.Model): listing = models.OneToOneField('Listing', related_name="contract", null=True, blank=True, default=None) handoffdate = models.DateTimeField(null=True, blank=True, default=None) returndate = models.DateTimeField(null=True, blank=True, default=None) class AgentAgreement(models.Model): book = models.ForeignKey(BookInstance, related_name='delegation') class Listing(models.Model): book_instance = models.ForeignKey(BookInstance, related_name='listings')
Attachments (2)
Change History (10)
by , 9 years ago
comment:1 by , 9 years ago
Component: | Documentation → Database layer (models, ORM) |
---|---|
Triage Stage: | Unreviewed → Accepted |
Reproduce at f945fb24a31737c6625205a8cd90eabdf1c33584 with the attached test app.
by , 9 years ago
Attachment: | t26551.tar.gz added |
---|
comment:4 by , 8 years ago
Patch needs improvement: | unset |
---|
comment:5 by , 8 years ago
Cc: | added |
---|
comment:8 by , 8 years ago
Version: | 1.9 → master |
---|
This has been merged on master 5 days ago, so Django 2.0 should include this fix.
Note:
See TracTickets
for help on using tickets.
models.py (with comment that has command to run on shell to trigger behavior)