mirror of
https://github.com/django/django.git
synced 2024-12-01 07:31:48 +01:00
b81e974e9e
An n-ary logical XOR Q(…) ^ Q(…) ^ … ^ Q(…) should evaluate to true when an odd number of its operands evaluate to true, not when exactly one operand evaluates to true.
89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
from django.db.models import Q
|
|
from django.test import TestCase
|
|
|
|
from .models import Number
|
|
|
|
|
|
class XorLookupsTests(TestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.numbers = [Number.objects.create(num=i) for i in range(10)]
|
|
|
|
def test_filter(self):
|
|
self.assertCountEqual(
|
|
Number.objects.filter(num__lte=7) ^ Number.objects.filter(num__gte=3),
|
|
self.numbers[:3] + self.numbers[8:],
|
|
)
|
|
self.assertCountEqual(
|
|
Number.objects.filter(Q(num__lte=7) ^ Q(num__gte=3)),
|
|
self.numbers[:3] + self.numbers[8:],
|
|
)
|
|
|
|
def test_filter_multiple(self):
|
|
qs = Number.objects.filter(
|
|
Q(num__gte=1)
|
|
^ Q(num__gte=3)
|
|
^ Q(num__gte=5)
|
|
^ Q(num__gte=7)
|
|
^ Q(num__gte=9)
|
|
)
|
|
self.assertCountEqual(
|
|
qs,
|
|
self.numbers[1:3] + self.numbers[5:7] + self.numbers[9:],
|
|
)
|
|
self.assertCountEqual(
|
|
qs.values_list("num", flat=True),
|
|
[
|
|
i
|
|
for i in range(10)
|
|
if (i >= 1) ^ (i >= 3) ^ (i >= 5) ^ (i >= 7) ^ (i >= 9)
|
|
],
|
|
)
|
|
|
|
def test_filter_negated(self):
|
|
self.assertCountEqual(
|
|
Number.objects.filter(Q(num__lte=7) ^ ~Q(num__lt=3)),
|
|
self.numbers[:3] + self.numbers[8:],
|
|
)
|
|
self.assertCountEqual(
|
|
Number.objects.filter(~Q(num__gt=7) ^ ~Q(num__lt=3)),
|
|
self.numbers[:3] + self.numbers[8:],
|
|
)
|
|
self.assertCountEqual(
|
|
Number.objects.filter(Q(num__lte=7) ^ ~Q(num__lt=3) ^ Q(num__lte=1)),
|
|
[self.numbers[2]] + self.numbers[8:],
|
|
)
|
|
self.assertCountEqual(
|
|
Number.objects.filter(~(Q(num__lte=7) ^ ~Q(num__lt=3) ^ Q(num__lte=1))),
|
|
self.numbers[:2] + self.numbers[3:8],
|
|
)
|
|
|
|
def test_exclude(self):
|
|
self.assertCountEqual(
|
|
Number.objects.exclude(Q(num__lte=7) ^ Q(num__gte=3)),
|
|
self.numbers[3:8],
|
|
)
|
|
|
|
def test_stages(self):
|
|
numbers = Number.objects.all()
|
|
self.assertSequenceEqual(
|
|
numbers.filter(num__gte=0) ^ numbers.filter(num__lte=11),
|
|
[],
|
|
)
|
|
self.assertSequenceEqual(
|
|
numbers.filter(num__gt=0) ^ numbers.filter(num__lt=11),
|
|
[self.numbers[0]],
|
|
)
|
|
|
|
def test_pk_q(self):
|
|
self.assertCountEqual(
|
|
Number.objects.filter(Q(pk=self.numbers[0].pk) ^ Q(pk=self.numbers[1].pk)),
|
|
self.numbers[:2],
|
|
)
|
|
|
|
def test_empty_in(self):
|
|
self.assertCountEqual(
|
|
Number.objects.filter(Q(pk__in=[]) ^ Q(num__gte=5)),
|
|
self.numbers[5:],
|
|
)
|