Opened 9 years ago

Closed 9 years ago

#24986 closed Bug (fixed)

Selecting distinct on lower() function throws AttributeError("'Func' object has no attribute 'column'")

Reported by: Wojciech Bartosiak Owned by: Valentina Mukhamedzhanova
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: F(), ORM, distinct
Cc: josh.smeaton@… 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 (last modified by Wojciech Bartosiak)

I have simple table:

class Author(models.Model):
    name = models.CharField(max_length=40)

I want to write Query using Django ORM to be similar to:

SELECT DISTINCT LOWER(name) from my_app_author;

I finally ended with:

Author.objects.annotate(
    name_lower=Func(F('name'), function='lower')
).distinct('name_lower')

But I'm receiving error:

Traceback (most recent call last):
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/query.py", line 138, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/query.py", line 162, in __iter__
    self._fetch_all()
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/query.py", line 965, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/query.py", line 238, in iterator
    results = compiler.execute_sql()
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 829, in execute_sql
    sql, params = self.as_sql()
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 381, in as_sql
    distinct_fields = self.get_distinct()
  File "/opt/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 545, in get_distinct
    result.append("%s.%s" % (qn(alias), qn2(target.column)))
AttributeError: 'Func' object has no attribute 'column'

but when i write:

Author.objects.annotate(
    name_lower=Func(F('name'), function='lower')
).distinct('something')

I'm receiving an error:

django.core.exceptions.FieldError: 
Cannot resolve keyword 'something' into field. 
Choices are: id, name, name_lower, src_id

As database driver I'm using 'ENGINE': 'django.contrib.gis.db.backends.postgis'

Change History (14)

comment:1 by Wojciech Bartosiak, 9 years ago

comment:2 by Wojciech Bartosiak, 9 years ago

Description: modified (diff)

comment:3 by Josh Smeaton, 9 years ago

Severity: Release blockerNormal
Triage Stage: UnreviewedAccepted
Version: 1.8master
Author.objects.annotate(
    name_lower=Func(F('name'), function='lower')
).distinct('name_lower')

I don't think we implemented Expression support for distinct(). Accepting on that basis - it shouldn't (famous last words..) be too difficult to implement.

but when i write:

Author.objects.annotate(
    name_lower=Func(F('name'), function='lower')
).distinct('something')

I've removed release blocker and 1.8 target, as this is a new feature not previously available. It should be able to go into 1.9 provided there's a patch, but I'm not sure that we'd backport a fix to 1.8.

I don't know why you'd think that'd work, especially since the error tells you that "something" isn't actually a field on the model. Am I missing something here?

comment:4 by Anssi Kääriäinen, 9 years ago

+1 to non-release-blocker. It would be nice to have this, but I don't think this is a severe limitation in the expressions feature. In other words, this is just an ordinary bug.

comment:5 by Wojciech Bartosiak, 9 years ago

I don't know why you'd think that'd work, especially since the error tells you that "something" isn't actually a field on the model. Am I missing something here?

So why You can see name_lower on the choices list ?

Choices are: id, name, name_lower, src_id

comment:6 by Josh Smeaton, 9 years ago

Because name_lower is added to the field list when you use the annotation. The field "something" does not exist and was not annotated in your example.

comment:7 by Shai Berger, 9 years ago

I believe the OP was only using something to point out that name_lower doesn't work in spite of being given as an option in the error message.

On a separate note: You should be able to get the query you described initially with

Author.objects.annotate(
    name_lower=Func(F('name'), function='lower')
).values('name_lower').distinct()

or something similar.

comment:8 by Wojciech Bartosiak, 9 years ago

Thx,
@shaib this worked for me :)

Last edited 9 years ago by Wojciech Bartosiak (previous) (diff)

comment:9 by Valentina Mukhamedzhanova, 9 years ago

Owner: changed from nobody to Valentina Mukhamedzhanova
Status: newassigned

comment:10 by Josh Smeaton, 9 years ago

Cc: josh.smeaton@… added

comment:11 by Tim Graham, 9 years ago

Has patch: set

comment:12 by Tim Graham, 9 years ago

I think the patch looks okay (I proposed some cosmetic improvements to the test). Josh, could you check it?

comment:13 by Tim Graham, 9 years ago

Triage Stage: AcceptedReady for checkin

comment:14 by Tim Graham <timograham@…>, 9 years ago

Resolution: fixed
Status: assignedclosed

In 1f7b25c:

Fixed #24986 -- Added support for annotations in DISTINCT queries.

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