Skip to content

Commit

Permalink
Refs django#373 -- Updated TupleIsNull lookup to check if any is NULL…
Browse files Browse the repository at this point in the history
… rather than all.

Regression in 1eac690.
  • Loading branch information
csirmazbendeguz authored and sarahboyce committed Sep 20, 2024
1 parent 1857b66 commit c2c7dbb
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 15 deletions.
27 changes: 17 additions & 10 deletions django/db/models/fields/tuple_lookups.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,25 @@ def as_oracle(self, compiler, connection):
return root.as_sql(compiler, connection)


class TupleIsNull(IsNull):
class TupleIsNull(TupleLookupMixin, IsNull):
def get_prep_lookup(self):
rhs = self.rhs
if isinstance(rhs, (tuple, list)) and len(rhs) == 1:
rhs = rhs[0]
if isinstance(rhs, bool):
return rhs
raise ValueError(
"The QuerySet value for an isnull lookup must be True or False."
)

def as_sql(self, compiler, connection):
# e.g.: (a, b, c) is None as SQL:
# WHERE a IS NULL AND b IS NULL AND c IS NULL
vals = self.rhs
if isinstance(vals, bool):
vals = [vals] * len(self.lhs)

cols = self.lhs.get_cols()
lookups = [IsNull(col, val) for col, val in zip(cols, vals)]
root = WhereNode(lookups, connector=AND)

# WHERE a IS NULL OR b IS NULL OR c IS NULL
# e.g.: (a, b, c) is not None as SQL:
# WHERE a IS NOT NULL AND b IS NOT NULL AND c IS NOT NULL
rhs = self.rhs
lookups = [IsNull(col, rhs) for col in self.lhs]
root = WhereNode(lookups, connector=OR if rhs else AND)
return root.as_sql(compiler, connection)


Expand Down
2 changes: 1 addition & 1 deletion tests/foreign_object/models/person.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __str__(self):

class Membership(models.Model):
# Table Column Fields
membership_country = models.ForeignKey(Country, models.CASCADE)
membership_country = models.ForeignKey(Country, models.CASCADE, null=True)
date_joined = models.DateTimeField(default=datetime.datetime.now)
invite_reason = models.CharField(max_length=64, null=True)
person_id = models.IntegerField()
Expand Down
25 changes: 21 additions & 4 deletions tests/foreign_object/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,18 +516,35 @@ def test_batch_create_foreign_object(self):

def test_isnull_lookup(self):
m1 = Membership.objects.create(
membership_country=self.usa, person=self.bob, group_id=None
person_id=self.bob.id,
membership_country_id=self.usa.id,
group_id=None,
)
m2 = Membership.objects.create(
membership_country=self.usa, person=self.bob, group=self.cia
person_id=self.jim.id,
membership_country_id=None,
group_id=self.cia.id,
)
m3 = Membership.objects.create(
person_id=self.jane.id,
membership_country_id=None,
group_id=None,
)
m4 = Membership.objects.create(
person_id=self.george.id,
membership_country_id=self.soviet_union.id,
group_id=self.kgb.id,
)
for member in [m1, m2, m3]:
with self.assertRaises(Membership.group.RelatedObjectDoesNotExist):
getattr(member, "group")
self.assertSequenceEqual(
Membership.objects.filter(group__isnull=True),
[m1],
[m1, m2, m3],
)
self.assertSequenceEqual(
Membership.objects.filter(group__isnull=False),
[m2],
[m4],
)


Expand Down

0 comments on commit c2c7dbb

Please sign in to comment.