mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #25367 -- Allowed boolean expressions in QuerySet.filter() and exclude().
This allows using expressions that have an output_field that is a BooleanField to be used directly in a queryset filters, or in the When() clauses of a Case() expression. Thanks Josh Smeaton, Tim Graham, Simon Charette, Mariusz Felisiak, and Adam Johnson for reviews. Co-Authored-By: NyanKiyoshi <hello@vanille.bid>
This commit is contained in:
committed by
Mariusz Felisiak
parent
069bee7c12
commit
4137fc2efc
@@ -37,7 +37,7 @@ class BasicExpressionsTests(TestCase):
|
||||
ceo=Employee.objects.create(firstname="Joe", lastname="Smith", salary=10)
|
||||
)
|
||||
cls.foobar_ltd = Company.objects.create(
|
||||
name="Foobar Ltd.", num_employees=3, num_chairs=4,
|
||||
name="Foobar Ltd.", num_employees=3, num_chairs=4, based_in_eu=True,
|
||||
ceo=Employee.objects.create(firstname="Frank", lastname="Meyer", salary=20)
|
||||
)
|
||||
cls.max = Employee.objects.create(firstname='Max', lastname='Mustermann', salary=30)
|
||||
@@ -83,6 +83,14 @@ class BasicExpressionsTests(TestCase):
|
||||
2,
|
||||
)
|
||||
|
||||
def test_filtering_on_q_that_is_boolean(self):
|
||||
self.assertEqual(
|
||||
Company.objects.filter(
|
||||
ExpressionWrapper(Q(num_employees__gt=3), output_field=models.BooleanField())
|
||||
).count(),
|
||||
2,
|
||||
)
|
||||
|
||||
def test_filter_inter_attribute(self):
|
||||
# We can filter on attribute relationships on same model obj, e.g.
|
||||
# find companies where the number of employees is greater
|
||||
@@ -642,6 +650,56 @@ class BasicExpressionsTests(TestCase):
|
||||
with self.assertRaisesMessage(FieldError, "Cannot resolve keyword 'nope' into field."):
|
||||
list(Company.objects.filter(ceo__pk=F('point_of_contact__nope')))
|
||||
|
||||
def test_exists_in_filter(self):
|
||||
inner = Company.objects.filter(ceo=OuterRef('pk')).values('pk')
|
||||
qs1 = Employee.objects.filter(Exists(inner))
|
||||
qs2 = Employee.objects.annotate(found=Exists(inner)).filter(found=True)
|
||||
self.assertCountEqual(qs1, qs2)
|
||||
self.assertFalse(Employee.objects.exclude(Exists(inner)).exists())
|
||||
self.assertCountEqual(qs2, Employee.objects.exclude(~Exists(inner)))
|
||||
|
||||
def test_subquery_in_filter(self):
|
||||
inner = Company.objects.filter(ceo=OuterRef('pk')).values('based_in_eu')
|
||||
self.assertSequenceEqual(
|
||||
Employee.objects.filter(Subquery(inner)),
|
||||
[self.foobar_ltd.ceo],
|
||||
)
|
||||
|
||||
def test_case_in_filter_if_boolean_output_field(self):
|
||||
is_ceo = Company.objects.filter(ceo=OuterRef('pk'))
|
||||
is_poc = Company.objects.filter(point_of_contact=OuterRef('pk'))
|
||||
qs = Employee.objects.filter(
|
||||
Case(
|
||||
When(Exists(is_ceo), then=True),
|
||||
When(Exists(is_poc), then=True),
|
||||
default=False,
|
||||
output_field=models.BooleanField(),
|
||||
),
|
||||
)
|
||||
self.assertSequenceEqual(qs, [self.example_inc.ceo, self.foobar_ltd.ceo, self.max])
|
||||
|
||||
def test_boolean_expression_combined(self):
|
||||
is_ceo = Company.objects.filter(ceo=OuterRef('pk'))
|
||||
is_poc = Company.objects.filter(point_of_contact=OuterRef('pk'))
|
||||
self.gmbh.point_of_contact = self.max
|
||||
self.gmbh.save()
|
||||
self.assertSequenceEqual(
|
||||
Employee.objects.filter(Exists(is_ceo) | Exists(is_poc)),
|
||||
[self.example_inc.ceo, self.foobar_ltd.ceo, self.max],
|
||||
)
|
||||
self.assertSequenceEqual(
|
||||
Employee.objects.filter(Exists(is_ceo) & Exists(is_poc)),
|
||||
[self.max],
|
||||
)
|
||||
self.assertSequenceEqual(
|
||||
Employee.objects.filter(Exists(is_ceo) & Q(salary__gte=30)),
|
||||
[self.max],
|
||||
)
|
||||
self.assertSequenceEqual(
|
||||
Employee.objects.filter(Exists(is_poc) | Q(salary__lt=15)),
|
||||
[self.example_inc.ceo, self.max],
|
||||
)
|
||||
|
||||
|
||||
class IterableLookupInnerExpressionsTests(TestCase):
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user