Opened 4 years ago
Last modified 3 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)