Opened 17 months ago

Last modified 2 weeks ago

#27021 assigned New feature

Add explicit support for Q object annotations

Reported by: Josh Smeaton Owned by: Sergey Fedoseev
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: josh.smeaton@… Triage Stage: Accepted
Has patch: no Needs documentation: yes
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

A common pattern that has arisen with expressions is to annotate a Q object to the query without going through the Case/When ceremony.

Model.objects.annotate(boolfield=ExpressionWrapper(Q(field__gte=4), output_field=BooleanField()))

We've tried to discourage this pattern because the Case/When structure is a lot more powerful and correct. Sometimes the power isn't required and users really just want to annotate a boolean expression. Q objects and the WhereNode it resolves to aren't really proper expressions, but they could be. Case/When uses Q objects internally, which is why it's important to test Case/When for any major changes to Q and WhereNode.

Explicit support for Q object annotations should implement an implicit BooleanField as the output_field. I believe this is only required on the WhereNode since that is what is ultimately resolved. Once this happens, the above query becomes a lot nicer.

Model.objects.annotate(boolfield=Q(field__gte=4))

For this to work the following is definitely required:

  • Add an output_field=BooleanField() to WhereNode
  • Add a resolve_expression() to WhereNode (unsure what this should return - requires significant testing with Case/When)

Might be required:

  • Add an output_field=BooleanField() to Q

Tests:

  • Adding a subquery containing a Q annotation to a parent query
  • Adding a subquery containing a Case/When annotation to a parent query (if it's not already tested)
  • Standard tests that most other annotations already implement
  • Nullable fields used in Q annotations

Change History (4)

comment:1 Changed 16 months ago by Ian Foote

Owner: changed from nobody to Ian Foote
Status: newassigned

comment:2 Changed 16 months ago by Ian Foote

I took a quick look at this and ran into circular import problems when setting output_field=BooleanField() or importing Expression (both from django.db.models). This will need some careful thought about how WhereNode and Q interact.

Last edited 16 months ago by Ian Foote (previous) (diff)

comment:3 Changed 16 months ago by Ian Foote

Owner: Ian Foote deleted
Status: assignednew

comment:4 Changed 2 weeks ago by Sergey Fedoseev

Owner: set to Sergey Fedoseev
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top