Opened 3 years ago
Last modified 2 years ago
#33137 closed New feature
Decouple Field.unique from select_related — at Version 1
Reported by: | Markus Holtermann | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | |
Severity: | Normal | Keywords: | |
Cc: | Simon Charette | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When inheriting from a OneToOneField
that automatically adds additional constraints to a model, the object-level relation might be a one-to-one relation, while the underlying implementation is still a many-to-one.
Let's say you have two models A
and B
where A.rel = MyOneToOneField(B)
and A.deleted = BooleanField()
. In a regular OneToOneField
there would now be a unique index on A.rel
. However, by adding a unique constraint to A._meta.constraints
over rel
with a condition on deleted=False
, rel
only needs to be unique among the "undeleted" objects. Doing so is possible by forcing MyOneToOneField.unique=False
(otherwise migrations create a constraint). However, this will mean, SQLCompiler.get_related_selections()
fails when trying to do B.objects.select_related("a")
, since a
is not a valid field. That is because SQLCompiler.get_related_selections._get_field_choices()
uses f.field.unique
instead of (f.field.many_to_one or f.field.one_to_one)
, I think. Because, essentially, opts.related_objects
is build based on those (many|one)_to_(many|one)
field attributes.
I think, what would fix the behavior, could be something like this:
class SQLCompiler: def get_related_selections(self, select, opts=None, root_alias=None, cur_depth=1, requested=None, restricted=None): def _get_field_choices(): direct_choices = (f.name for f in opts.fields if f.is_relation) reverse_choices = ( f.field.related_query_name() for f in opts.related_objects if (f.field.many_to_one or f.field.one_to_one) ) return chain(direct_choices, reverse_choices, self.query._filtered_relations)