Opened 3 years ago

Last modified 18 months ago

#33050 closed Bug

calling count on union of queries having select_related results in error. — at Version 4

Reported by: Sunkyue Owned by: nobody
Component: Database layer (models, ORM) Version: 3.2
Severity: Normal Keywords: orm, count, union, select_related mysql
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Sunkyue)

test case:

class ModelA(models.Model):
    value = models.IntegerField()

class ModelB(models.Model):
   model_a = models.ForeignKey("ModelA", on_delete=models.CASCADE)

->

ModelB.objects.select_related('model_a').union(ModelB.objects.select_related('model_a')).count()

results in error.

 File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\models\query.py", line 412, in count
    return self.query.get_count(using=self.db)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\models\sql\query.py", line 521, in get_count
    number = obj.get_aggregation(using, ['__count'])['__count']
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\models\sql\query.py", line 506, in get_aggregation
    result = compiler.execute_sql(SINGLE)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\models\sql\compiler.py", line 1175, in execute_sql
    cursor.execute(sql, params)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\utils.py", line 98, in execute
    return super().execute(sql, params)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\django\db\backends\mysql\base.py", line 73, in execute
    return self.cursor.execute(query, args)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\MySQLdb\cursors.py", line 206, in execute
    res = self._query(query)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\MySQLdb\cursors.py", line 319, in _query
    db.query(q)
  File "c:\Users\user\Documents\workspace\realclass2-api\.venv\lib\site-packages\MySQLdb\connections.py", line 259, in query
    _mysql.connection.query(self, query)
django.db.utils.OperationalError: (1060, "Duplicate column name 'id'")

suggested fix:
on django.db.models.sql.query.py -> Query.get_aggregation, from

            inner_query.select_for_update = False
            inner_query.select_related = False
            inner_query.set_annotation_mask(self.annotation_select)

to

            inner_query.select_for_update = False
            inner_query.select_related = False
            for combined_query in inner_query.combined_queries:
                combined_query.select_related = False
            inner_query.set_annotation_mask(self.annotation_select)

can solve the problem I think.

my current monkey-patching code is,

from django.db.models.sql.query import Query

old_get_aggregation = Query.get_aggregation

def get_aggregation(self, using, added_aggregate_names):
    original_select_related_values = {}
    for combined_query in self.combined_queries:
        original_select_related_values[combined_query] = combined_query.select_related
        combined_query.select_related = False
    result = old_get_aggregation(self, using, added_aggregate_names)
    for combined_query in self.combined_queries:
        combined_query.select_related = original_select_related_values[combined_query]
    return result

Query.get_aggregation = get_aggregation

reproduced on on 3.2.5/3.2.6

Change History (4)

comment:1 by Sunkyue, 3 years ago

Easy pickings: set

comment:2 by Sunkyue, 3 years ago

Description: modified (diff)

comment:3 by Sunkyue, 3 years ago

Description: modified (diff)

comment:4 by Sunkyue, 3 years ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top