summaryrefslogtreecommitdiff
path: root/tests/xor_lookups
diff options
context:
space:
mode:
authorRyan Heard <ryanwheard@gmail.com>2021-07-02 15:09:13 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-03-04 12:55:37 +0100
commitc6b4d62fa2c7f73b87f6ae7e8cf1d64ee5312dc5 (patch)
tree238bd8c3045c8d4577e09bd913de9748dcd49968 /tests/xor_lookups
parent795da6306a048b18c0158496b0d49e8e4f197a32 (diff)
downloaddjango-c6b4d62fa2c7f73b87f6ae7e8cf1d64ee5312dc5.tar.gz
Fixed #29865 -- Added logical XOR support for Q() and querysets.
Diffstat (limited to 'tests/xor_lookups')
-rw-r--r--tests/xor_lookups/__init__.py0
-rw-r--r--tests/xor_lookups/models.py8
-rw-r--r--tests/xor_lookups/tests.py67
3 files changed, 75 insertions, 0 deletions
diff --git a/tests/xor_lookups/__init__.py b/tests/xor_lookups/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/xor_lookups/__init__.py
diff --git a/tests/xor_lookups/models.py b/tests/xor_lookups/models.py
new file mode 100644
index 0000000000..22e79aa94f
--- /dev/null
+++ b/tests/xor_lookups/models.py
@@ -0,0 +1,8 @@
+from django.db import models
+
+
+class Number(models.Model):
+ num = models.IntegerField()
+
+ def __str__(self):
+ return str(self.num)
diff --git a/tests/xor_lookups/tests.py b/tests/xor_lookups/tests.py
new file mode 100644
index 0000000000..a9cdf9cb31
--- /dev/null
+++ b/tests/xor_lookups/tests.py
@@ -0,0 +1,67 @@
+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_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:],
+ )