|
| 1 | +import unittest |
1 | 2 | from datetime import date
|
| 3 | +from operator import attrgetter |
2 | 4 |
|
3 | 5 | from django.core.exceptions import FieldDoesNotExist
|
4 | 6 | from django.db import connection, models
|
| 7 | +from django.db.models.expressions import Value |
5 | 8 | from django.test import SimpleTestCase, TestCase
|
6 | 9 | from django.test.utils import CaptureQueriesContext, isolate_apps
|
7 | 10 |
|
8 |
| -from django_mongodb_backend.fields import EmbeddedModelArrayField |
| 11 | +from django_mongodb_backend.fields import ArrayField, EmbeddedModelArrayField |
9 | 12 | from django_mongodb_backend.models import EmbeddedModel
|
10 | 13 |
|
11 |
| -from .models import Artifact, Exhibit, Movie, Restoration, Review, Section, Tour |
| 14 | +from .models import Artifact, Audit, Exhibit, Movie, Restoration, Review, Section, Tour |
12 | 15 |
|
13 | 16 |
|
14 | 17 | class MethodTests(SimpleTestCase):
|
@@ -116,6 +119,7 @@ def setUpTestData(cls):
|
116 | 119 | ],
|
117 | 120 | )
|
118 | 121 | ],
|
| 122 | + main_section=Section(number=2), |
119 | 123 | )
|
120 | 124 | cls.lost_empires = Exhibit.objects.create(
|
121 | 125 | name="Lost Empires",
|
@@ -146,6 +150,9 @@ def setUpTestData(cls):
|
146 | 150 | cls.egypt_tour = Tour.objects.create(guide="Amira", exhibit=cls.egypt)
|
147 | 151 | cls.wonders_tour = Tour.objects.create(guide="Carlos", exhibit=cls.wonders)
|
148 | 152 | cls.lost_tour = Tour.objects.create(guide="Yelena", exhibit=cls.lost_empires)
|
| 153 | + cls.audit_1 = Audit.objects.create(section_number=1, reviewed=True) |
| 154 | + cls.audit_2 = Audit.objects.create(section_number=2, reviewed=True) |
| 155 | + cls.audit_3 = Audit.objects.create(section_number=5, reviewed=False) |
149 | 156 |
|
150 | 157 | def test_exact(self):
|
151 | 158 | self.assertCountEqual(
|
@@ -284,6 +291,71 @@ def test_foreign_field_with_slice(self):
|
284 | 291 | qs = Tour.objects.filter(exhibit__sections__0_2__number__in=[1, 2])
|
285 | 292 | self.assertCountEqual(qs, [self.wonders_tour, self.egypt_tour])
|
286 | 293 |
|
| 294 | + def test_subquery_numeric_lookups(self): |
| 295 | + subquery = Audit.objects.filter( |
| 296 | + section_number__in=models.OuterRef("sections__number") |
| 297 | + ).values("section_number")[:1] |
| 298 | + tests = [ |
| 299 | + ("exact", [self.egypt, self.new_descoveries, self.wonders]), |
| 300 | + ("lt", []), |
| 301 | + ("lte", [self.egypt, self.new_descoveries, self.wonders]), |
| 302 | + ("gt", [self.wonders]), |
| 303 | + ("gte", [self.egypt, self.new_descoveries, self.wonders]), |
| 304 | + ] |
| 305 | + for lookup, expected in tests: |
| 306 | + with self.subTest(lookup=lookup): |
| 307 | + kwargs = {f"sections__number__{lookup}": subquery} |
| 308 | + self.assertCountEqual(Exhibit.objects.filter(**kwargs), expected) |
| 309 | + |
| 310 | + def test_subquery_in_lookup(self): |
| 311 | + subquery = Audit.objects.filter(reviewed=True).values_list("section_number", flat=True) |
| 312 | + result = Exhibit.objects.filter(sections__number__in=subquery) |
| 313 | + self.assertCountEqual(result, [self.wonders, self.new_descoveries, self.egypt]) |
| 314 | + |
| 315 | + def test_array_as_rhs(self): |
| 316 | + result = Exhibit.objects.filter(main_section__number__in=models.F("sections__number")) |
| 317 | + self.assertCountEqual(result, [self.new_descoveries]) |
| 318 | + |
| 319 | + def test_array_annotation_lookup(self): |
| 320 | + result = Exhibit.objects.annotate(section_numbers=models.F("main_section__number")).filter( |
| 321 | + section_numbers__in=models.F("sections__number") |
| 322 | + ) |
| 323 | + self.assertCountEqual(result, [self.new_descoveries]) |
| 324 | + |
| 325 | + def test_array_as_rhs_for_arrayfield_lookups(self): |
| 326 | + tests = [ |
| 327 | + ("exact", [self.wonders]), |
| 328 | + ("lt", [self.new_descoveries]), |
| 329 | + ("lte", [self.wonders, self.new_descoveries]), |
| 330 | + ("gt", [self.egypt, self.lost_empires]), |
| 331 | + ("gte", [self.egypt, self.wonders, self.lost_empires]), |
| 332 | + ("overlap", [self.egypt, self.wonders, self.new_descoveries]), |
| 333 | + ("contained_by", [self.wonders]), |
| 334 | + ("contains", [self.egypt, self.wonders, self.new_descoveries, self.lost_empires]), |
| 335 | + ] |
| 336 | + for lookup, expected in tests: |
| 337 | + with self.subTest(lookup=lookup): |
| 338 | + kwargs = {f"section_numbers__{lookup}": models.F("sections__number")} |
| 339 | + result = Exhibit.objects.annotate( |
| 340 | + section_numbers=Value( |
| 341 | + [1, 2], output_field=ArrayField(base_field=models.IntegerField()) |
| 342 | + ) |
| 343 | + ).filter(**kwargs) |
| 344 | + self.assertCountEqual(result, expected) |
| 345 | + |
| 346 | + @unittest.expectedFailure |
| 347 | + def test_array_annotation_index(self): |
| 348 | + # Slicing and indexing over an annotated EmbeddedModelArrayField would |
| 349 | + # require a refactor of annotation handling. |
| 350 | + result = Exhibit.objects.annotate(section_numbers=models.F("sections__number")).filter( |
| 351 | + section_numbers__0=1 |
| 352 | + ) |
| 353 | + self.assertCountEqual(result, [self.new_descoveries, self.egypt]) |
| 354 | + |
| 355 | + def test_array_annotation(self): |
| 356 | + qs = Exhibit.objects.annotate(section_numbers=models.F("sections__number")).order_by("name") |
| 357 | + self.assertQuerySetEqual(qs, [[1], [], [2], [1, 2]], attrgetter("section_numbers")) |
| 358 | + |
287 | 359 |
|
288 | 360 | @isolate_apps("model_fields_")
|
289 | 361 | class CheckTests(SimpleTestCase):
|
|
0 commit comments