Opened 3 days ago

Last modified 45 hours ago

#36149 assigned Bug

Composite primary key subquery lookup prevent usage or specify fields and are not implemented for exact

Reported by: Simon Charette Owned by: Simon Charette
Component: Database layer (models, ORM) Version: 5.2
Severity: Release blocker Keywords:
Cc: Csirmaz Bendegúz Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

I started playing with tuple lookup support for composite primary key and I noticed two main problems.

First pk__in=query lookups completely disallow specifying which fields should be used in the select clause of the right-hand-side and implicitly set them to the left-hand-side name which assumes the same set of left-hand-side field name is shared by the right-hand-side which is a bad assumption

  • tests/composite_pk/models/tenant.py

    diff --git a/tests/composite_pk/models/tenant.py b/tests/composite_pk/models/tenant.py
    index 6286ed2354..c85869afa7 100644
    a b class Comment(models.Model):  
    4444        related_name="comments",
    4545    )
    4646    text = models.TextField(default="", blank=True)
     47    integer = models.IntegerField(default=0)
    4748
    4849
    4950class Post(models.Model):
  • tests/composite_pk/test_filter.py

    diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
    index fe942b9e5b..78383655a0 100644
    a b def test_filter_comments_by_pk_in(self):  
    182182                    Comment.objects.filter(pk__in=pks).order_by("pk"), objs
    183183                )
    184184
     185    def test_filter_comments_by_pk_in_subquery(self):
     186        self.assertSequenceEqual(
     187            Comment.objects.filter(
     188                pk__in=Comment.objects.filter(pk=self.comment_1.pk),
     189            ),
     190            [self.comment_1],
     191        )
     192        self.assertSequenceEqual(
     193            Comment.objects.filter(
     194                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
     195                    "tenant_id", "id"
     196                ),
     197            ),
     198            [self.comment_1],
     199        )
     200        self.comment_2.integer = self.comment_1.id
     201        self.comment_2.save()
     202        self.assertSequenceEqual(
     203            Comment.objects.filter(
     204                pk__in=Comment.objects.values("tenant_id", "integer"),
     205            ),
     206            [self.comment_1],
     207        )
     208
    185209    def test_filter_comments_by_user_and_order_by_pk_asc(self):
    186210        self.assertSequenceEqual(
    187211            Comment.objects.filter(user=self.user_1).order_by("pk"),

Allowing fields to be specified requires lifting a QuerySet resolving time constraint which was entirely by-passable by passing queryset.query instead as the right-hand-side.

The tuple exact lookup should also allow querysets with a single element to be specified as right-hand-side which is currently disallowed

  • tests/composite_pk/test_filter.py

    diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
    index fe942b9e5b..78383655a0 100644
    a b def test_non_outer_ref_subquery(self):  
    450474        with self.assertRaisesMessage(ValueError, msg):
    451475            Comment.objects.filter(pk=pk)
    452476
     477    def test_filter_comments_by_pk_exact_subquery(self):
     478        self.assertSequenceEqual(
     479            Comment.objects.filter(
     480                pk=Comment.objects.filter(pk=self.comment_1.pk)[:1],
     481            ),
     482            [self.comment_1],
     483        )
     484        self.assertSequenceEqual(
     485            Comment.objects.filter(
     486                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
     487                    "tenant_id", "id"
     488                )[:1],
     489            ),
     490            [self.comment_1],
     491        )
     492        self.comment_2.integer = self.comment_1.id
     493        self.comment_2.save()
     494        self.assertSequenceEqual(
     495            Comment.objects.filter(
     496                pk__in=Comment.objects.values("tenant_id", "integer"),
     497            )[:1],
     498            [self.comment_1],
     499        )
     500
    453501    def test_outer_ref_not_composite_pk(self):
    454502        subquery = Comment.objects.filter(pk=OuterRef("id")).values("id")
    455503        queryset = Comment.objects.filter(id=Subquery(subquery))

Lastly lifting the constraint that prevented subqueries returning more than one column from being used as a right-hand-side required adjusting the existing exact and in lookup logic to disallow left-hand-side and right-hand-side with mismatching number of fields.

Change History (3)

comment:1 by Sarah Boyce, 3 days ago

Cc: Csirmaz Bendegúz added
Has patch: unset
Owner: set to Simon Charette
Triage Stage: UnreviewedAccepted

Thank you!

comment:2 by Sarah Boyce, 3 days ago

Has patch: set

comment:3 by Sarah Boyce, 45 hours ago

Patch needs improvement: set
Note: See TracTickets for help on using tickets.
Back to Top