Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#18248 closed Bug (fixed)

Determining SQL statement for a proxy model breaks count

Reported by: zmsmith Owned by: akaariai
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords: count, proxy
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Given a proxy model Foo:

query = Foo.objects.all()
query.count()

5

query_str = str(query)
query.count()

DatabaseError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near

Change History (10)

comment:1 Changed 3 years ago by zmsmith

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

query = Foo.objects.all()
query.count()
# returns correctly
query_str = str(query)
query.count()
# raises DataBaseError

comment:2 Changed 3 years ago by zmsmith

Having a lot of trouble with the formatting. Hopefully it's still clear.

comment:3 Changed 3 years ago by akaariai

  • Owner changed from nobody to akaariai
  • Triage Stage changed from Unreviewed to Accepted

Hint: use {{{code here}}} for formatting code blocks.

I can verify this, happens on SQLite too. This does not happen when not using proxy-models. Test setup:

class DumbCategory(models.Model):
    pass

class ProxyCategory(DumbCategory):
    class Meta:
        proxy = True

def test_evaluated_proxy_count(self):
    ProxyCategory.objects.create()
    qs = ProxyCategory.objects.all()
    self.assertEqual(qs.count(), 1)
    str(qs.query)
    self.assertEqual(qs.count(), 1)

The problem seems to be related to str(qs.query) generating the query string without actually executing it. If you execute the query instead of generating the query string the second qs.count() will just see the cached results and thus not execute a query at all. No error in that case.

comment:4 Changed 3 years ago by akaariai

I think I nailed this one - the reason was that proxy model's parent tables were added multiple times to included_inherited_models in sql/query.py. The included_inherited_models seems to be meant only for multitable inheritance. The mistake caused the proxy model table to be removed multiple times from the query in remove_inherited_models().

See pull request https://github.com/django/django/pull/32 for patch.

comment:5 Changed 3 years ago by akaariai

  • Resolution set to fixed
  • Status changed from new to closed

comment:6 Changed 3 years ago by Anssi Kääriäinen

Fix proxy model Query.remove_inherited_models()

Fixed #18248 -- proxy models were added to included_inherited_models
in sql.query.Query. The variable is meant to be used for multitable
inheritance only. This mistake caused problems in situations where
proxy model's query was reused.

Changeset: c2e1ecb4b136016ab2996712aa0583fd91657bc9

comment:7 Changed 3 years ago by Anssi Kääriäinen

Fix proxy model Query.remove_inherited_models()

Fixed #18248 -- proxy models were added to included_inherited_models
in sql.query.Query. The variable is meant to be used for multitable
inheritance only. This mistake caused problems in situations where
proxy model's query was reused.

Changeset: c2e1ecb4b136016ab2996712aa0583fd91657bc9

comment:8 Changed 3 years ago by Anssi Kääriäinen

Fix proxy model Query.remove_inherited_models()

Fixed #18248 -- proxy models were added to included_inherited_models
in sql.query.Query. The variable is meant to be used for multitable
inheritance only. This mistake caused problems in situations where
proxy model's query was reused.

Changeset: c2e1ecb4b136016ab2996712aa0583fd91657bc9

comment:9 follow-up: Changed 3 years ago by aaugustin

Sorry for the spam -- GitHub integration in progress.

comment:10 in reply to: ↑ 9 Changed 3 years ago by cdawson@…

Replying to aaugustin:

I hit this error on my upgrade to Django 1.4 getting the error:

DatabaseError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near at line 1")

My code was: self._cached_total = self._queryset.count()
And unfortunately: self._queryset.all().count() didn't help. Same error.

I worked around it by changing my code to:

self._cached_total = self._queryset.values("id").count()

Just figured I'd post it here in case it saved someone else some headaches.

-Cam

Note: See TracTickets for help on using tickets.
Back to Top