Opened 8 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 , 8 years ago
Summary: | Cannot annotate exisiting field → Allow QuerySet.annotate() to use the name of an exisiting field |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → New feature |
comment:2 by , 8 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 , 5 years ago
Thank you. But please note that there is no possibility to use expressions with extra(select={..}).
comment:4 by , 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 , 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:
- 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.
- 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 , 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.
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.