Opened 7 years ago

Last modified 2 years ago

#28072 new New feature

Allow QuerySet.annotate() to use the name of an exisiting field

Reported by: Marcin Nowak Owned by: nobody
Component: Database layer (models, ORM) Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Because of this exception https://github.com/django/django/blob/master/django/db/models/query.py#L900 I can't add annotation.
I need to >rename< db_field within the query and fetch results by .values() / .values_list(), so mapping db values to fields is not required.

Queries like "select x as y, y as x from table" are valid, and as a Django user I want to do similar things using queryset API, for my own responsibility.

Please add possibility to bypass the constraint somehow, or move this constraint elsewhere and activiate it when no .values()/.values_list() are used. Also please backport the change to the 1.8.x branch.

Thank you.

Change History (6)

comment:1 by Tim Graham, 7 years ago

Summary: Cannot annotate exisiting fieldAllow QuerySet.annotate() to use the name of an exisiting field
Triage Stage: UnreviewedAccepted
Type: BugNew feature

I'm not sure if this is feasible or a good idea, but if you provide a patch, we can take a look. Per our supported versions policy, it won't be backported to 1.8.

comment:2 by Marcin Nowak, 7 years ago

Please note that .extra(select={'x': 'y'}) works without limitation and can be used as a workaround for .annotate()'s conflict exception.

comment:3 by Marcin Nowak, 4 years ago

Thank you. But please note that there is no possibility to use expressions with extra(select={..}).

comment:4 by David, 4 years ago

For what it's worth this would be really handy. I took a look, but adding this kind of functionality is likely beyond me.

comment:5 by Evan Fiddler, 2 years ago

Is there still interest from anyone else on this? I accomplished this is an abstracted version of QuerySet that my company uses internally. Approved to make a PR against Django Project if there’s interest.

The one main concern I have on this idea is in two parts:

  1. In order to successfully annotate the queryset with a name that’s already used (and for it to be at all useful) the original value must be removed from the queryset.
  2. This is very doable by pulling current values, removing the “old” key, then applying the annotate to the queryset. All very doable.

However, this returns an incredibly unsafe queryset which if we see in anything but a read-only way could overwrite data in the model/db.

So, here’s my solution: make ANOTHER method (annotate_override() or annotate_read_only()or something like that) that returns the annotated queryset with the old name/value removed and the passed-in expression annotated.

One issue I see with this is that it’s returning a true queryset and therefore, in order for THIS to work well, we may need another abstracted class on QuerySet like ReadOnlyQuerySet(QuerySet) or something like that…which makes this a much larger change.

Alternatively, the doc string on this new method could be incredibly verbose with warnings about usage.

Lastly, we could just provide a k, v param to annotate() (e.g. annotate(<annotation expression>, override_model=True) and the change is smaller but less safe.

If this sound good, I’ll assign and make the PR. I’d like some feedback on this first though.

comment:6 by Aleksander, 2 years ago

I'm interested, I sometimes need things like: qs = qs.annotate(sales=F('sales') * F("price")) and for me it's only readonly usecase.
I can work around it with qs.values().annotate() maybe this is more clean approach.

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