1. import decimal
    
  2. import enum
    
  3. import json
    
  4. import unittest
    
  5. import uuid
    
  6. 
    
  7. from django import forms
    
  8. from django.core import checks, exceptions, serializers, validators
    
  9. from django.core.exceptions import FieldError
    
  10. from django.core.management import call_command
    
  11. from django.db import IntegrityError, connection, models
    
  12. from django.db.models.expressions import Exists, OuterRef, RawSQL, Value
    
  13. from django.db.models.functions import Cast, JSONObject, Upper
    
  14. from django.test import TransactionTestCase, modify_settings, override_settings
    
  15. from django.test.utils import isolate_apps
    
  16. from django.utils import timezone
    
  17. 
    
  18. from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase, PostgreSQLWidgetTestCase
    
  19. from .models import (
    
  20.     ArrayEnumModel,
    
  21.     ArrayFieldSubclass,
    
  22.     CharArrayModel,
    
  23.     DateTimeArrayModel,
    
  24.     IntegerArrayModel,
    
  25.     NestedIntegerArrayModel,
    
  26.     NullableIntegerArrayModel,
    
  27.     OtherTypesArrayModel,
    
  28.     PostgreSQLModel,
    
  29.     Tag,
    
  30. )
    
  31. 
    
  32. try:
    
  33.     from psycopg2.extras import NumericRange
    
  34. 
    
  35.     from django.contrib.postgres.aggregates import ArrayAgg
    
  36.     from django.contrib.postgres.expressions import ArraySubquery
    
  37.     from django.contrib.postgres.fields import ArrayField
    
  38.     from django.contrib.postgres.fields.array import IndexTransform, SliceTransform
    
  39.     from django.contrib.postgres.forms import (
    
  40.         SimpleArrayField,
    
  41.         SplitArrayField,
    
  42.         SplitArrayWidget,
    
  43.     )
    
  44. except ImportError:
    
  45.     pass
    
  46. 
    
  47. 
    
  48. @isolate_apps("postgres_tests")
    
  49. class BasicTests(PostgreSQLSimpleTestCase):
    
  50.     def test_get_field_display(self):
    
  51.         class MyModel(PostgreSQLModel):
    
  52.             field = ArrayField(
    
  53.                 models.CharField(max_length=16),
    
  54.                 choices=[
    
  55.                     ["Media", [(["vinyl", "cd"], "Audio")]],
    
  56.                     (("mp3", "mp4"), "Digital"),
    
  57.                 ],
    
  58.             )
    
  59. 
    
  60.         tests = (
    
  61.             (["vinyl", "cd"], "Audio"),
    
  62.             (("mp3", "mp4"), "Digital"),
    
  63.             (("a", "b"), "('a', 'b')"),
    
  64.             (["c", "d"], "['c', 'd']"),
    
  65.         )
    
  66.         for value, display in tests:
    
  67.             with self.subTest(value=value, display=display):
    
  68.                 instance = MyModel(field=value)
    
  69.                 self.assertEqual(instance.get_field_display(), display)
    
  70. 
    
  71.     def test_get_field_display_nested_array(self):
    
  72.         class MyModel(PostgreSQLModel):
    
  73.             field = ArrayField(
    
  74.                 ArrayField(models.CharField(max_length=16)),
    
  75.                 choices=[
    
  76.                     [
    
  77.                         "Media",
    
  78.                         [([["vinyl", "cd"], ("x",)], "Audio")],
    
  79.                     ],
    
  80.                     ((["mp3"], ("mp4",)), "Digital"),
    
  81.                 ],
    
  82.             )
    
  83. 
    
  84.         tests = (
    
  85.             ([["vinyl", "cd"], ("x",)], "Audio"),
    
  86.             ((["mp3"], ("mp4",)), "Digital"),
    
  87.             ((("a", "b"), ("c",)), "(('a', 'b'), ('c',))"),
    
  88.             ([["a", "b"], ["c"]], "[['a', 'b'], ['c']]"),
    
  89.         )
    
  90.         for value, display in tests:
    
  91.             with self.subTest(value=value, display=display):
    
  92.                 instance = MyModel(field=value)
    
  93.                 self.assertEqual(instance.get_field_display(), display)
    
  94. 
    
  95. 
    
  96. class TestSaveLoad(PostgreSQLTestCase):
    
  97.     def test_integer(self):
    
  98.         instance = IntegerArrayModel(field=[1, 2, 3])
    
  99.         instance.save()
    
  100.         loaded = IntegerArrayModel.objects.get()
    
  101.         self.assertEqual(instance.field, loaded.field)
    
  102. 
    
  103.     def test_char(self):
    
  104.         instance = CharArrayModel(field=["hello", "goodbye"])
    
  105.         instance.save()
    
  106.         loaded = CharArrayModel.objects.get()
    
  107.         self.assertEqual(instance.field, loaded.field)
    
  108. 
    
  109.     def test_dates(self):
    
  110.         instance = DateTimeArrayModel(
    
  111.             datetimes=[timezone.now()],
    
  112.             dates=[timezone.now().date()],
    
  113.             times=[timezone.now().time()],
    
  114.         )
    
  115.         instance.save()
    
  116.         loaded = DateTimeArrayModel.objects.get()
    
  117.         self.assertEqual(instance.datetimes, loaded.datetimes)
    
  118.         self.assertEqual(instance.dates, loaded.dates)
    
  119.         self.assertEqual(instance.times, loaded.times)
    
  120. 
    
  121.     def test_tuples(self):
    
  122.         instance = IntegerArrayModel(field=(1,))
    
  123.         instance.save()
    
  124.         loaded = IntegerArrayModel.objects.get()
    
  125.         self.assertSequenceEqual(instance.field, loaded.field)
    
  126. 
    
  127.     def test_integers_passed_as_strings(self):
    
  128.         # This checks that get_prep_value is deferred properly
    
  129.         instance = IntegerArrayModel(field=["1"])
    
  130.         instance.save()
    
  131.         loaded = IntegerArrayModel.objects.get()
    
  132.         self.assertEqual(loaded.field, [1])
    
  133. 
    
  134.     def test_default_null(self):
    
  135.         instance = NullableIntegerArrayModel()
    
  136.         instance.save()
    
  137.         loaded = NullableIntegerArrayModel.objects.get(pk=instance.pk)
    
  138.         self.assertIsNone(loaded.field)
    
  139.         self.assertEqual(instance.field, loaded.field)
    
  140. 
    
  141.     def test_null_handling(self):
    
  142.         instance = NullableIntegerArrayModel(field=None)
    
  143.         instance.save()
    
  144.         loaded = NullableIntegerArrayModel.objects.get()
    
  145.         self.assertEqual(instance.field, loaded.field)
    
  146. 
    
  147.         instance = IntegerArrayModel(field=None)
    
  148.         with self.assertRaises(IntegrityError):
    
  149.             instance.save()
    
  150. 
    
  151.     def test_nested(self):
    
  152.         instance = NestedIntegerArrayModel(field=[[1, 2], [3, 4]])
    
  153.         instance.save()
    
  154.         loaded = NestedIntegerArrayModel.objects.get()
    
  155.         self.assertEqual(instance.field, loaded.field)
    
  156. 
    
  157.     def test_other_array_types(self):
    
  158.         instance = OtherTypesArrayModel(
    
  159.             ips=["192.168.0.1", "::1"],
    
  160.             uuids=[uuid.uuid4()],
    
  161.             decimals=[decimal.Decimal(1.25), 1.75],
    
  162.             tags=[Tag(1), Tag(2), Tag(3)],
    
  163.             json=[{"a": 1}, {"b": 2}],
    
  164.             int_ranges=[NumericRange(10, 20), NumericRange(30, 40)],
    
  165.             bigint_ranges=[
    
  166.                 NumericRange(7000000000, 10000000000),
    
  167.                 NumericRange(50000000000, 70000000000),
    
  168.             ],
    
  169.         )
    
  170.         instance.save()
    
  171.         loaded = OtherTypesArrayModel.objects.get()
    
  172.         self.assertEqual(instance.ips, loaded.ips)
    
  173.         self.assertEqual(instance.uuids, loaded.uuids)
    
  174.         self.assertEqual(instance.decimals, loaded.decimals)
    
  175.         self.assertEqual(instance.tags, loaded.tags)
    
  176.         self.assertEqual(instance.json, loaded.json)
    
  177.         self.assertEqual(instance.int_ranges, loaded.int_ranges)
    
  178.         self.assertEqual(instance.bigint_ranges, loaded.bigint_ranges)
    
  179. 
    
  180.     def test_null_from_db_value_handling(self):
    
  181.         instance = OtherTypesArrayModel.objects.create(
    
  182.             ips=["192.168.0.1", "::1"],
    
  183.             uuids=[uuid.uuid4()],
    
  184.             decimals=[decimal.Decimal(1.25), 1.75],
    
  185.             tags=None,
    
  186.         )
    
  187.         instance.refresh_from_db()
    
  188.         self.assertIsNone(instance.tags)
    
  189.         self.assertEqual(instance.json, [])
    
  190.         self.assertIsNone(instance.int_ranges)
    
  191.         self.assertIsNone(instance.bigint_ranges)
    
  192. 
    
  193.     def test_model_set_on_base_field(self):
    
  194.         instance = IntegerArrayModel()
    
  195.         field = instance._meta.get_field("field")
    
  196.         self.assertEqual(field.model, IntegerArrayModel)
    
  197.         self.assertEqual(field.base_field.model, IntegerArrayModel)
    
  198. 
    
  199.     def test_nested_nullable_base_field(self):
    
  200.         instance = NullableIntegerArrayModel.objects.create(
    
  201.             field_nested=[[None, None], [None, None]],
    
  202.         )
    
  203.         self.assertEqual(instance.field_nested, [[None, None], [None, None]])
    
  204. 
    
  205. 
    
  206. class TestQuerying(PostgreSQLTestCase):
    
  207.     @classmethod
    
  208.     def setUpTestData(cls):
    
  209.         cls.objs = NullableIntegerArrayModel.objects.bulk_create(
    
  210.             [
    
  211.                 NullableIntegerArrayModel(order=1, field=[1]),
    
  212.                 NullableIntegerArrayModel(order=2, field=[2]),
    
  213.                 NullableIntegerArrayModel(order=3, field=[2, 3]),
    
  214.                 NullableIntegerArrayModel(order=4, field=[20, 30, 40]),
    
  215.                 NullableIntegerArrayModel(order=5, field=None),
    
  216.             ]
    
  217.         )
    
  218. 
    
  219.     def test_empty_list(self):
    
  220.         NullableIntegerArrayModel.objects.create(field=[])
    
  221.         obj = (
    
  222.             NullableIntegerArrayModel.objects.annotate(
    
  223.                 empty_array=models.Value(
    
  224.                     [], output_field=ArrayField(models.IntegerField())
    
  225.                 ),
    
  226.             )
    
  227.             .filter(field=models.F("empty_array"))
    
  228.             .get()
    
  229.         )
    
  230.         self.assertEqual(obj.field, [])
    
  231.         self.assertEqual(obj.empty_array, [])
    
  232. 
    
  233.     def test_exact(self):
    
  234.         self.assertSequenceEqual(
    
  235.             NullableIntegerArrayModel.objects.filter(field__exact=[1]), self.objs[:1]
    
  236.         )
    
  237. 
    
  238.     def test_exact_with_expression(self):
    
  239.         self.assertSequenceEqual(
    
  240.             NullableIntegerArrayModel.objects.filter(field__exact=[Value(1)]),
    
  241.             self.objs[:1],
    
  242.         )
    
  243. 
    
  244.     def test_exact_charfield(self):
    
  245.         instance = CharArrayModel.objects.create(field=["text"])
    
  246.         self.assertSequenceEqual(
    
  247.             CharArrayModel.objects.filter(field=["text"]), [instance]
    
  248.         )
    
  249. 
    
  250.     def test_exact_nested(self):
    
  251.         instance = NestedIntegerArrayModel.objects.create(field=[[1, 2], [3, 4]])
    
  252.         self.assertSequenceEqual(
    
  253.             NestedIntegerArrayModel.objects.filter(field=[[1, 2], [3, 4]]), [instance]
    
  254.         )
    
  255. 
    
  256.     def test_isnull(self):
    
  257.         self.assertSequenceEqual(
    
  258.             NullableIntegerArrayModel.objects.filter(field__isnull=True), self.objs[-1:]
    
  259.         )
    
  260. 
    
  261.     def test_gt(self):
    
  262.         self.assertSequenceEqual(
    
  263.             NullableIntegerArrayModel.objects.filter(field__gt=[0]), self.objs[:4]
    
  264.         )
    
  265. 
    
  266.     def test_lt(self):
    
  267.         self.assertSequenceEqual(
    
  268.             NullableIntegerArrayModel.objects.filter(field__lt=[2]), self.objs[:1]
    
  269.         )
    
  270. 
    
  271.     def test_in(self):
    
  272.         self.assertSequenceEqual(
    
  273.             NullableIntegerArrayModel.objects.filter(field__in=[[1], [2]]),
    
  274.             self.objs[:2],
    
  275.         )
    
  276. 
    
  277.     def test_in_subquery(self):
    
  278.         IntegerArrayModel.objects.create(field=[2, 3])
    
  279.         self.assertSequenceEqual(
    
  280.             NullableIntegerArrayModel.objects.filter(
    
  281.                 field__in=IntegerArrayModel.objects.values_list("field", flat=True)
    
  282.             ),
    
  283.             self.objs[2:3],
    
  284.         )
    
  285. 
    
  286.     @unittest.expectedFailure
    
  287.     def test_in_including_F_object(self):
    
  288.         # This test asserts that Array objects passed to filters can be
    
  289.         # constructed to contain F objects. This currently doesn't work as the
    
  290.         # psycopg2 mogrify method that generates the ARRAY() syntax is
    
  291.         # expecting literals, not column references (#27095).
    
  292.         self.assertSequenceEqual(
    
  293.             NullableIntegerArrayModel.objects.filter(field__in=[[models.F("id")]]),
    
  294.             self.objs[:2],
    
  295.         )
    
  296. 
    
  297.     def test_in_as_F_object(self):
    
  298.         self.assertSequenceEqual(
    
  299.             NullableIntegerArrayModel.objects.filter(field__in=[models.F("field")]),
    
  300.             self.objs[:4],
    
  301.         )
    
  302. 
    
  303.     def test_contained_by(self):
    
  304.         self.assertSequenceEqual(
    
  305.             NullableIntegerArrayModel.objects.filter(field__contained_by=[1, 2]),
    
  306.             self.objs[:2],
    
  307.         )
    
  308. 
    
  309.     def test_contained_by_including_F_object(self):
    
  310.         self.assertSequenceEqual(
    
  311.             NullableIntegerArrayModel.objects.filter(
    
  312.                 field__contained_by=[models.F("order"), 2]
    
  313.             ),
    
  314.             self.objs[:3],
    
  315.         )
    
  316. 
    
  317.     def test_contains(self):
    
  318.         self.assertSequenceEqual(
    
  319.             NullableIntegerArrayModel.objects.filter(field__contains=[2]),
    
  320.             self.objs[1:3],
    
  321.         )
    
  322. 
    
  323.     def test_contains_subquery(self):
    
  324.         IntegerArrayModel.objects.create(field=[2, 3])
    
  325.         inner_qs = IntegerArrayModel.objects.values_list("field", flat=True)
    
  326.         self.assertSequenceEqual(
    
  327.             NullableIntegerArrayModel.objects.filter(field__contains=inner_qs[:1]),
    
  328.             self.objs[2:3],
    
  329.         )
    
  330.         inner_qs = IntegerArrayModel.objects.filter(field__contains=OuterRef("field"))
    
  331.         self.assertSequenceEqual(
    
  332.             NullableIntegerArrayModel.objects.filter(Exists(inner_qs)),
    
  333.             self.objs[1:3],
    
  334.         )
    
  335. 
    
  336.     def test_contains_including_expression(self):
    
  337.         self.assertSequenceEqual(
    
  338.             NullableIntegerArrayModel.objects.filter(
    
  339.                 field__contains=[2, Value(6) / Value(2)],
    
  340.             ),
    
  341.             self.objs[2:3],
    
  342.         )
    
  343. 
    
  344.     def test_icontains(self):
    
  345.         # Using the __icontains lookup with ArrayField is inefficient.
    
  346.         instance = CharArrayModel.objects.create(field=["FoO"])
    
  347.         self.assertSequenceEqual(
    
  348.             CharArrayModel.objects.filter(field__icontains="foo"), [instance]
    
  349.         )
    
  350. 
    
  351.     def test_contains_charfield(self):
    
  352.         # Regression for #22907
    
  353.         self.assertSequenceEqual(
    
  354.             CharArrayModel.objects.filter(field__contains=["text"]), []
    
  355.         )
    
  356. 
    
  357.     def test_contained_by_charfield(self):
    
  358.         self.assertSequenceEqual(
    
  359.             CharArrayModel.objects.filter(field__contained_by=["text"]), []
    
  360.         )
    
  361. 
    
  362.     def test_overlap_charfield(self):
    
  363.         self.assertSequenceEqual(
    
  364.             CharArrayModel.objects.filter(field__overlap=["text"]), []
    
  365.         )
    
  366. 
    
  367.     def test_overlap_charfield_including_expression(self):
    
  368.         obj_1 = CharArrayModel.objects.create(field=["TEXT", "lower text"])
    
  369.         obj_2 = CharArrayModel.objects.create(field=["lower text", "TEXT"])
    
  370.         CharArrayModel.objects.create(field=["lower text", "text"])
    
  371.         self.assertSequenceEqual(
    
  372.             CharArrayModel.objects.filter(
    
  373.                 field__overlap=[
    
  374.                     Upper(Value("text")),
    
  375.                     "other",
    
  376.                 ]
    
  377.             ),
    
  378.             [obj_1, obj_2],
    
  379.         )
    
  380. 
    
  381.     def test_lookups_autofield_array(self):
    
  382.         qs = (
    
  383.             NullableIntegerArrayModel.objects.filter(
    
  384.                 field__0__isnull=False,
    
  385.             )
    
  386.             .values("field__0")
    
  387.             .annotate(
    
  388.                 arrayagg=ArrayAgg("id"),
    
  389.             )
    
  390.             .order_by("field__0")
    
  391.         )
    
  392.         tests = (
    
  393.             ("contained_by", [self.objs[1].pk, self.objs[2].pk, 0], [2]),
    
  394.             ("contains", [self.objs[2].pk], [2]),
    
  395.             ("exact", [self.objs[3].pk], [20]),
    
  396.             ("overlap", [self.objs[1].pk, self.objs[3].pk], [2, 20]),
    
  397.         )
    
  398.         for lookup, value, expected in tests:
    
  399.             with self.subTest(lookup=lookup):
    
  400.                 self.assertSequenceEqual(
    
  401.                     qs.filter(
    
  402.                         **{"arrayagg__" + lookup: value},
    
  403.                     ).values_list("field__0", flat=True),
    
  404.                     expected,
    
  405.                 )
    
  406. 
    
  407.     def test_index(self):
    
  408.         self.assertSequenceEqual(
    
  409.             NullableIntegerArrayModel.objects.filter(field__0=2), self.objs[1:3]
    
  410.         )
    
  411. 
    
  412.     def test_index_chained(self):
    
  413.         self.assertSequenceEqual(
    
  414.             NullableIntegerArrayModel.objects.filter(field__0__lt=3), self.objs[0:3]
    
  415.         )
    
  416. 
    
  417.     def test_index_nested(self):
    
  418.         instance = NestedIntegerArrayModel.objects.create(field=[[1, 2], [3, 4]])
    
  419.         self.assertSequenceEqual(
    
  420.             NestedIntegerArrayModel.objects.filter(field__0__0=1), [instance]
    
  421.         )
    
  422. 
    
  423.     @unittest.expectedFailure
    
  424.     def test_index_used_on_nested_data(self):
    
  425.         instance = NestedIntegerArrayModel.objects.create(field=[[1, 2], [3, 4]])
    
  426.         self.assertSequenceEqual(
    
  427.             NestedIntegerArrayModel.objects.filter(field__0=[1, 2]), [instance]
    
  428.         )
    
  429. 
    
  430.     def test_index_transform_expression(self):
    
  431.         expr = RawSQL("string_to_array(%s, ';')", ["1;2"])
    
  432.         self.assertSequenceEqual(
    
  433.             NullableIntegerArrayModel.objects.filter(
    
  434.                 field__0=Cast(
    
  435.                     IndexTransform(1, models.IntegerField, expr),
    
  436.                     output_field=models.IntegerField(),
    
  437.                 ),
    
  438.             ),
    
  439.             self.objs[:1],
    
  440.         )
    
  441. 
    
  442.     def test_index_annotation(self):
    
  443.         qs = NullableIntegerArrayModel.objects.annotate(second=models.F("field__1"))
    
  444.         self.assertCountEqual(
    
  445.             qs.values_list("second", flat=True),
    
  446.             [None, None, None, 3, 30],
    
  447.         )
    
  448. 
    
  449.     def test_overlap(self):
    
  450.         self.assertSequenceEqual(
    
  451.             NullableIntegerArrayModel.objects.filter(field__overlap=[1, 2]),
    
  452.             self.objs[0:3],
    
  453.         )
    
  454. 
    
  455.     def test_len(self):
    
  456.         self.assertSequenceEqual(
    
  457.             NullableIntegerArrayModel.objects.filter(field__len__lte=2), self.objs[0:3]
    
  458.         )
    
  459. 
    
  460.     def test_len_empty_array(self):
    
  461.         obj = NullableIntegerArrayModel.objects.create(field=[])
    
  462.         self.assertSequenceEqual(
    
  463.             NullableIntegerArrayModel.objects.filter(field__len=0), [obj]
    
  464.         )
    
  465. 
    
  466.     def test_slice(self):
    
  467.         self.assertSequenceEqual(
    
  468.             NullableIntegerArrayModel.objects.filter(field__0_1=[2]), self.objs[1:3]
    
  469.         )
    
  470. 
    
  471.         self.assertSequenceEqual(
    
  472.             NullableIntegerArrayModel.objects.filter(field__0_2=[2, 3]), self.objs[2:3]
    
  473.         )
    
  474. 
    
  475.     def test_order_by_slice(self):
    
  476.         more_objs = (
    
  477.             NullableIntegerArrayModel.objects.create(field=[1, 637]),
    
  478.             NullableIntegerArrayModel.objects.create(field=[2, 1]),
    
  479.             NullableIntegerArrayModel.objects.create(field=[3, -98123]),
    
  480.             NullableIntegerArrayModel.objects.create(field=[4, 2]),
    
  481.         )
    
  482.         self.assertSequenceEqual(
    
  483.             NullableIntegerArrayModel.objects.order_by("field__1"),
    
  484.             [
    
  485.                 more_objs[2],
    
  486.                 more_objs[1],
    
  487.                 more_objs[3],
    
  488.                 self.objs[2],
    
  489.                 self.objs[3],
    
  490.                 more_objs[0],
    
  491.                 self.objs[4],
    
  492.                 self.objs[1],
    
  493.                 self.objs[0],
    
  494.             ],
    
  495.         )
    
  496. 
    
  497.     @unittest.expectedFailure
    
  498.     def test_slice_nested(self):
    
  499.         instance = NestedIntegerArrayModel.objects.create(field=[[1, 2], [3, 4]])
    
  500.         self.assertSequenceEqual(
    
  501.             NestedIntegerArrayModel.objects.filter(field__0__0_1=[1]), [instance]
    
  502.         )
    
  503. 
    
  504.     def test_slice_transform_expression(self):
    
  505.         expr = RawSQL("string_to_array(%s, ';')", ["9;2;3"])
    
  506.         self.assertSequenceEqual(
    
  507.             NullableIntegerArrayModel.objects.filter(
    
  508.                 field__0_2=SliceTransform(2, 3, expr)
    
  509.             ),
    
  510.             self.objs[2:3],
    
  511.         )
    
  512. 
    
  513.     def test_slice_annotation(self):
    
  514.         qs = NullableIntegerArrayModel.objects.annotate(
    
  515.             first_two=models.F("field__0_2"),
    
  516.         )
    
  517.         self.assertCountEqual(
    
  518.             qs.values_list("first_two", flat=True),
    
  519.             [None, [1], [2], [2, 3], [20, 30]],
    
  520.         )
    
  521. 
    
  522.     def test_usage_in_subquery(self):
    
  523.         self.assertSequenceEqual(
    
  524.             NullableIntegerArrayModel.objects.filter(
    
  525.                 id__in=NullableIntegerArrayModel.objects.filter(field__len=3)
    
  526.             ),
    
  527.             [self.objs[3]],
    
  528.         )
    
  529. 
    
  530.     def test_enum_lookup(self):
    
  531.         class TestEnum(enum.Enum):
    
  532.             VALUE_1 = "value_1"
    
  533. 
    
  534.         instance = ArrayEnumModel.objects.create(array_of_enums=[TestEnum.VALUE_1])
    
  535.         self.assertSequenceEqual(
    
  536.             ArrayEnumModel.objects.filter(array_of_enums__contains=[TestEnum.VALUE_1]),
    
  537.             [instance],
    
  538.         )
    
  539. 
    
  540.     def test_unsupported_lookup(self):
    
  541.         msg = (
    
  542.             "Unsupported lookup '0_bar' for ArrayField or join on the field not "
    
  543.             "permitted."
    
  544.         )
    
  545.         with self.assertRaisesMessage(FieldError, msg):
    
  546.             list(NullableIntegerArrayModel.objects.filter(field__0_bar=[2]))
    
  547. 
    
  548.         msg = (
    
  549.             "Unsupported lookup '0bar' for ArrayField or join on the field not "
    
  550.             "permitted."
    
  551.         )
    
  552.         with self.assertRaisesMessage(FieldError, msg):
    
  553.             list(NullableIntegerArrayModel.objects.filter(field__0bar=[2]))
    
  554. 
    
  555.     def test_grouping_by_annotations_with_array_field_param(self):
    
  556.         value = models.Value([1], output_field=ArrayField(models.IntegerField()))
    
  557.         self.assertEqual(
    
  558.             NullableIntegerArrayModel.objects.annotate(
    
  559.                 array_length=models.Func(
    
  560.                     value,
    
  561.                     1,
    
  562.                     function="ARRAY_LENGTH",
    
  563.                     output_field=models.IntegerField(),
    
  564.                 ),
    
  565.             )
    
  566.             .values("array_length")
    
  567.             .annotate(
    
  568.                 count=models.Count("pk"),
    
  569.             )
    
  570.             .get()["array_length"],
    
  571.             1,
    
  572.         )
    
  573. 
    
  574.     def test_filter_by_array_subquery(self):
    
  575.         inner_qs = NullableIntegerArrayModel.objects.filter(
    
  576.             field__len=models.OuterRef("field__len"),
    
  577.         ).values("field")
    
  578.         self.assertSequenceEqual(
    
  579.             NullableIntegerArrayModel.objects.alias(
    
  580.                 same_sized_fields=ArraySubquery(inner_qs),
    
  581.             ).filter(same_sized_fields__len__gt=1),
    
  582.             self.objs[0:2],
    
  583.         )
    
  584. 
    
  585.     def test_annotated_array_subquery(self):
    
  586.         inner_qs = NullableIntegerArrayModel.objects.exclude(
    
  587.             pk=models.OuterRef("pk")
    
  588.         ).values("order")
    
  589.         self.assertSequenceEqual(
    
  590.             NullableIntegerArrayModel.objects.annotate(
    
  591.                 sibling_ids=ArraySubquery(inner_qs),
    
  592.             )
    
  593.             .get(order=1)
    
  594.             .sibling_ids,
    
  595.             [2, 3, 4, 5],
    
  596.         )
    
  597. 
    
  598.     def test_group_by_with_annotated_array_subquery(self):
    
  599.         inner_qs = NullableIntegerArrayModel.objects.exclude(
    
  600.             pk=models.OuterRef("pk")
    
  601.         ).values("order")
    
  602.         self.assertSequenceEqual(
    
  603.             NullableIntegerArrayModel.objects.annotate(
    
  604.                 sibling_ids=ArraySubquery(inner_qs),
    
  605.                 sibling_count=models.Max("sibling_ids__len"),
    
  606.             ).values_list("sibling_count", flat=True),
    
  607.             [len(self.objs) - 1] * len(self.objs),
    
  608.         )
    
  609. 
    
  610.     def test_annotated_ordered_array_subquery(self):
    
  611.         inner_qs = NullableIntegerArrayModel.objects.order_by("-order").values("order")
    
  612.         self.assertSequenceEqual(
    
  613.             NullableIntegerArrayModel.objects.annotate(
    
  614.                 ids=ArraySubquery(inner_qs),
    
  615.             )
    
  616.             .first()
    
  617.             .ids,
    
  618.             [5, 4, 3, 2, 1],
    
  619.         )
    
  620. 
    
  621.     def test_annotated_array_subquery_with_json_objects(self):
    
  622.         inner_qs = NullableIntegerArrayModel.objects.exclude(
    
  623.             pk=models.OuterRef("pk")
    
  624.         ).values(json=JSONObject(order="order", field="field"))
    
  625.         siblings_json = (
    
  626.             NullableIntegerArrayModel.objects.annotate(
    
  627.                 siblings_json=ArraySubquery(inner_qs),
    
  628.             )
    
  629.             .values_list("siblings_json", flat=True)
    
  630.             .get(order=1)
    
  631.         )
    
  632.         self.assertSequenceEqual(
    
  633.             siblings_json,
    
  634.             [
    
  635.                 {"field": [2], "order": 2},
    
  636.                 {"field": [2, 3], "order": 3},
    
  637.                 {"field": [20, 30, 40], "order": 4},
    
  638.                 {"field": None, "order": 5},
    
  639.             ],
    
  640.         )
    
  641. 
    
  642. 
    
  643. class TestDateTimeExactQuerying(PostgreSQLTestCase):
    
  644.     @classmethod
    
  645.     def setUpTestData(cls):
    
  646.         now = timezone.now()
    
  647.         cls.datetimes = [now]
    
  648.         cls.dates = [now.date()]
    
  649.         cls.times = [now.time()]
    
  650.         cls.objs = [
    
  651.             DateTimeArrayModel.objects.create(
    
  652.                 datetimes=cls.datetimes, dates=cls.dates, times=cls.times
    
  653.             ),
    
  654.         ]
    
  655. 
    
  656.     def test_exact_datetimes(self):
    
  657.         self.assertSequenceEqual(
    
  658.             DateTimeArrayModel.objects.filter(datetimes=self.datetimes), self.objs
    
  659.         )
    
  660. 
    
  661.     def test_exact_dates(self):
    
  662.         self.assertSequenceEqual(
    
  663.             DateTimeArrayModel.objects.filter(dates=self.dates), self.objs
    
  664.         )
    
  665. 
    
  666.     def test_exact_times(self):
    
  667.         self.assertSequenceEqual(
    
  668.             DateTimeArrayModel.objects.filter(times=self.times), self.objs
    
  669.         )
    
  670. 
    
  671. 
    
  672. class TestOtherTypesExactQuerying(PostgreSQLTestCase):
    
  673.     @classmethod
    
  674.     def setUpTestData(cls):
    
  675.         cls.ips = ["192.168.0.1", "::1"]
    
  676.         cls.uuids = [uuid.uuid4()]
    
  677.         cls.decimals = [decimal.Decimal(1.25), 1.75]
    
  678.         cls.tags = [Tag(1), Tag(2), Tag(3)]
    
  679.         cls.objs = [
    
  680.             OtherTypesArrayModel.objects.create(
    
  681.                 ips=cls.ips,
    
  682.                 uuids=cls.uuids,
    
  683.                 decimals=cls.decimals,
    
  684.                 tags=cls.tags,
    
  685.             )
    
  686.         ]
    
  687. 
    
  688.     def test_exact_ip_addresses(self):
    
  689.         self.assertSequenceEqual(
    
  690.             OtherTypesArrayModel.objects.filter(ips=self.ips), self.objs
    
  691.         )
    
  692. 
    
  693.     def test_exact_uuids(self):
    
  694.         self.assertSequenceEqual(
    
  695.             OtherTypesArrayModel.objects.filter(uuids=self.uuids), self.objs
    
  696.         )
    
  697. 
    
  698.     def test_exact_decimals(self):
    
  699.         self.assertSequenceEqual(
    
  700.             OtherTypesArrayModel.objects.filter(decimals=self.decimals), self.objs
    
  701.         )
    
  702. 
    
  703.     def test_exact_tags(self):
    
  704.         self.assertSequenceEqual(
    
  705.             OtherTypesArrayModel.objects.filter(tags=self.tags), self.objs
    
  706.         )
    
  707. 
    
  708. 
    
  709. @isolate_apps("postgres_tests")
    
  710. class TestChecks(PostgreSQLSimpleTestCase):
    
  711.     def test_field_checks(self):
    
  712.         class MyModel(PostgreSQLModel):
    
  713.             field = ArrayField(models.CharField())
    
  714. 
    
  715.         model = MyModel()
    
  716.         errors = model.check()
    
  717.         self.assertEqual(len(errors), 1)
    
  718.         # The inner CharField is missing a max_length.
    
  719.         self.assertEqual(errors[0].id, "postgres.E001")
    
  720.         self.assertIn("max_length", errors[0].msg)
    
  721. 
    
  722.     def test_invalid_base_fields(self):
    
  723.         class MyModel(PostgreSQLModel):
    
  724.             field = ArrayField(
    
  725.                 models.ManyToManyField("postgres_tests.IntegerArrayModel")
    
  726.             )
    
  727. 
    
  728.         model = MyModel()
    
  729.         errors = model.check()
    
  730.         self.assertEqual(len(errors), 1)
    
  731.         self.assertEqual(errors[0].id, "postgres.E002")
    
  732. 
    
  733.     def test_invalid_default(self):
    
  734.         class MyModel(PostgreSQLModel):
    
  735.             field = ArrayField(models.IntegerField(), default=[])
    
  736. 
    
  737.         model = MyModel()
    
  738.         self.assertEqual(
    
  739.             model.check(),
    
  740.             [
    
  741.                 checks.Warning(
    
  742.                     msg=(
    
  743.                         "ArrayField default should be a callable instead of an "
    
  744.                         "instance so that it's not shared between all field "
    
  745.                         "instances."
    
  746.                     ),
    
  747.                     hint="Use a callable instead, e.g., use `list` instead of `[]`.",
    
  748.                     obj=MyModel._meta.get_field("field"),
    
  749.                     id="fields.E010",
    
  750.                 )
    
  751.             ],
    
  752.         )
    
  753. 
    
  754.     def test_valid_default(self):
    
  755.         class MyModel(PostgreSQLModel):
    
  756.             field = ArrayField(models.IntegerField(), default=list)
    
  757. 
    
  758.         model = MyModel()
    
  759.         self.assertEqual(model.check(), [])
    
  760. 
    
  761.     def test_valid_default_none(self):
    
  762.         class MyModel(PostgreSQLModel):
    
  763.             field = ArrayField(models.IntegerField(), default=None)
    
  764. 
    
  765.         model = MyModel()
    
  766.         self.assertEqual(model.check(), [])
    
  767. 
    
  768.     def test_nested_field_checks(self):
    
  769.         """
    
  770.         Nested ArrayFields are permitted.
    
  771.         """
    
  772. 
    
  773.         class MyModel(PostgreSQLModel):
    
  774.             field = ArrayField(ArrayField(models.CharField()))
    
  775. 
    
  776.         model = MyModel()
    
  777.         errors = model.check()
    
  778.         self.assertEqual(len(errors), 1)
    
  779.         # The inner CharField is missing a max_length.
    
  780.         self.assertEqual(errors[0].id, "postgres.E001")
    
  781.         self.assertIn("max_length", errors[0].msg)
    
  782. 
    
  783.     def test_choices_tuple_list(self):
    
  784.         class MyModel(PostgreSQLModel):
    
  785.             field = ArrayField(
    
  786.                 models.CharField(max_length=16),
    
  787.                 choices=[
    
  788.                     [
    
  789.                         "Media",
    
  790.                         [(["vinyl", "cd"], "Audio"), (("vhs", "dvd"), "Video")],
    
  791.                     ],
    
  792.                     (["mp3", "mp4"], "Digital"),
    
  793.                 ],
    
  794.             )
    
  795. 
    
  796.         self.assertEqual(MyModel._meta.get_field("field").check(), [])
    
  797. 
    
  798. 
    
  799. @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific tests")
    
  800. class TestMigrations(TransactionTestCase):
    
  801.     available_apps = ["postgres_tests"]
    
  802. 
    
  803.     def test_deconstruct(self):
    
  804.         field = ArrayField(models.IntegerField())
    
  805.         name, path, args, kwargs = field.deconstruct()
    
  806.         new = ArrayField(*args, **kwargs)
    
  807.         self.assertEqual(type(new.base_field), type(field.base_field))
    
  808.         self.assertIsNot(new.base_field, field.base_field)
    
  809. 
    
  810.     def test_deconstruct_with_size(self):
    
  811.         field = ArrayField(models.IntegerField(), size=3)
    
  812.         name, path, args, kwargs = field.deconstruct()
    
  813.         new = ArrayField(*args, **kwargs)
    
  814.         self.assertEqual(new.size, field.size)
    
  815. 
    
  816.     def test_deconstruct_args(self):
    
  817.         field = ArrayField(models.CharField(max_length=20))
    
  818.         name, path, args, kwargs = field.deconstruct()
    
  819.         new = ArrayField(*args, **kwargs)
    
  820.         self.assertEqual(new.base_field.max_length, field.base_field.max_length)
    
  821. 
    
  822.     def test_subclass_deconstruct(self):
    
  823.         field = ArrayField(models.IntegerField())
    
  824.         name, path, args, kwargs = field.deconstruct()
    
  825.         self.assertEqual(path, "django.contrib.postgres.fields.ArrayField")
    
  826. 
    
  827.         field = ArrayFieldSubclass()
    
  828.         name, path, args, kwargs = field.deconstruct()
    
  829.         self.assertEqual(path, "postgres_tests.models.ArrayFieldSubclass")
    
  830. 
    
  831.     @override_settings(
    
  832.         MIGRATION_MODULES={
    
  833.             "postgres_tests": "postgres_tests.array_default_migrations",
    
  834.         }
    
  835.     )
    
  836.     def test_adding_field_with_default(self):
    
  837.         # See #22962
    
  838.         table_name = "postgres_tests_integerarraydefaultmodel"
    
  839.         with connection.cursor() as cursor:
    
  840.             self.assertNotIn(table_name, connection.introspection.table_names(cursor))
    
  841.         call_command("migrate", "postgres_tests", verbosity=0)
    
  842.         with connection.cursor() as cursor:
    
  843.             self.assertIn(table_name, connection.introspection.table_names(cursor))
    
  844.         call_command("migrate", "postgres_tests", "zero", verbosity=0)
    
  845.         with connection.cursor() as cursor:
    
  846.             self.assertNotIn(table_name, connection.introspection.table_names(cursor))
    
  847. 
    
  848.     @override_settings(
    
  849.         MIGRATION_MODULES={
    
  850.             "postgres_tests": "postgres_tests.array_index_migrations",
    
  851.         }
    
  852.     )
    
  853.     def test_adding_arrayfield_with_index(self):
    
  854.         """
    
  855.         ArrayField shouldn't have varchar_patterns_ops or text_patterns_ops indexes.
    
  856.         """
    
  857.         table_name = "postgres_tests_chartextarrayindexmodel"
    
  858.         call_command("migrate", "postgres_tests", verbosity=0)
    
  859.         with connection.cursor() as cursor:
    
  860.             like_constraint_columns_list = [
    
  861.                 v["columns"]
    
  862.                 for k, v in list(
    
  863.                     connection.introspection.get_constraints(cursor, table_name).items()
    
  864.                 )
    
  865.                 if k.endswith("_like")
    
  866.             ]
    
  867.         # Only the CharField should have a LIKE index.
    
  868.         self.assertEqual(like_constraint_columns_list, [["char2"]])
    
  869.         # All fields should have regular indexes.
    
  870.         with connection.cursor() as cursor:
    
  871.             indexes = [
    
  872.                 c["columns"][0]
    
  873.                 for c in connection.introspection.get_constraints(
    
  874.                     cursor, table_name
    
  875.                 ).values()
    
  876.                 if c["index"] and len(c["columns"]) == 1
    
  877.             ]
    
  878.         self.assertIn("char", indexes)
    
  879.         self.assertIn("char2", indexes)
    
  880.         self.assertIn("text", indexes)
    
  881.         call_command("migrate", "postgres_tests", "zero", verbosity=0)
    
  882.         with connection.cursor() as cursor:
    
  883.             self.assertNotIn(table_name, connection.introspection.table_names(cursor))
    
  884. 
    
  885. 
    
  886. class TestSerialization(PostgreSQLSimpleTestCase):
    
  887.     test_data = (
    
  888.         '[{"fields": {"field": "[\\"1\\", \\"2\\", null]"}, '
    
  889.         '"model": "postgres_tests.integerarraymodel", "pk": null}]'
    
  890.     )
    
  891. 
    
  892.     def test_dumping(self):
    
  893.         instance = IntegerArrayModel(field=[1, 2, None])
    
  894.         data = serializers.serialize("json", [instance])
    
  895.         self.assertEqual(json.loads(data), json.loads(self.test_data))
    
  896. 
    
  897.     def test_loading(self):
    
  898.         instance = list(serializers.deserialize("json", self.test_data))[0].object
    
  899.         self.assertEqual(instance.field, [1, 2, None])
    
  900. 
    
  901. 
    
  902. class TestValidation(PostgreSQLSimpleTestCase):
    
  903.     def test_unbounded(self):
    
  904.         field = ArrayField(models.IntegerField())
    
  905.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  906.             field.clean([1, None], None)
    
  907.         self.assertEqual(cm.exception.code, "item_invalid")
    
  908.         self.assertEqual(
    
  909.             cm.exception.message % cm.exception.params,
    
  910.             "Item 2 in the array did not validate: This field cannot be null.",
    
  911.         )
    
  912. 
    
  913.     def test_blank_true(self):
    
  914.         field = ArrayField(models.IntegerField(blank=True, null=True))
    
  915.         # This should not raise a validation error
    
  916.         field.clean([1, None], None)
    
  917. 
    
  918.     def test_with_size(self):
    
  919.         field = ArrayField(models.IntegerField(), size=3)
    
  920.         field.clean([1, 2, 3], None)
    
  921.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  922.             field.clean([1, 2, 3, 4], None)
    
  923.         self.assertEqual(
    
  924.             cm.exception.messages[0],
    
  925.             "List contains 4 items, it should contain no more than 3.",
    
  926.         )
    
  927. 
    
  928.     def test_nested_array_mismatch(self):
    
  929.         field = ArrayField(ArrayField(models.IntegerField()))
    
  930.         field.clean([[1, 2], [3, 4]], None)
    
  931.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  932.             field.clean([[1, 2], [3, 4, 5]], None)
    
  933.         self.assertEqual(cm.exception.code, "nested_array_mismatch")
    
  934.         self.assertEqual(
    
  935.             cm.exception.messages[0], "Nested arrays must have the same length."
    
  936.         )
    
  937. 
    
  938.     def test_with_base_field_error_params(self):
    
  939.         field = ArrayField(models.CharField(max_length=2))
    
  940.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  941.             field.clean(["abc"], None)
    
  942.         self.assertEqual(len(cm.exception.error_list), 1)
    
  943.         exception = cm.exception.error_list[0]
    
  944.         self.assertEqual(
    
  945.             exception.message,
    
  946.             "Item 1 in the array did not validate: Ensure this value has at most 2 "
    
  947.             "characters (it has 3).",
    
  948.         )
    
  949.         self.assertEqual(exception.code, "item_invalid")
    
  950.         self.assertEqual(
    
  951.             exception.params,
    
  952.             {"nth": 1, "value": "abc", "limit_value": 2, "show_value": 3},
    
  953.         )
    
  954. 
    
  955.     def test_with_validators(self):
    
  956.         field = ArrayField(
    
  957.             models.IntegerField(validators=[validators.MinValueValidator(1)])
    
  958.         )
    
  959.         field.clean([1, 2], None)
    
  960.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  961.             field.clean([0], None)
    
  962.         self.assertEqual(len(cm.exception.error_list), 1)
    
  963.         exception = cm.exception.error_list[0]
    
  964.         self.assertEqual(
    
  965.             exception.message,
    
  966.             "Item 1 in the array did not validate: Ensure this value is greater than "
    
  967.             "or equal to 1.",
    
  968.         )
    
  969.         self.assertEqual(exception.code, "item_invalid")
    
  970.         self.assertEqual(
    
  971.             exception.params, {"nth": 1, "value": 0, "limit_value": 1, "show_value": 0}
    
  972.         )
    
  973. 
    
  974. 
    
  975. class TestSimpleFormField(PostgreSQLSimpleTestCase):
    
  976.     def test_valid(self):
    
  977.         field = SimpleArrayField(forms.CharField())
    
  978.         value = field.clean("a,b,c")
    
  979.         self.assertEqual(value, ["a", "b", "c"])
    
  980. 
    
  981.     def test_to_python_fail(self):
    
  982.         field = SimpleArrayField(forms.IntegerField())
    
  983.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  984.             field.clean("a,b,9")
    
  985.         self.assertEqual(
    
  986.             cm.exception.messages[0],
    
  987.             "Item 1 in the array did not validate: Enter a whole number.",
    
  988.         )
    
  989. 
    
  990.     def test_validate_fail(self):
    
  991.         field = SimpleArrayField(forms.CharField(required=True))
    
  992.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  993.             field.clean("a,b,")
    
  994.         self.assertEqual(
    
  995.             cm.exception.messages[0],
    
  996.             "Item 3 in the array did not validate: This field is required.",
    
  997.         )
    
  998. 
    
  999.     def test_validate_fail_base_field_error_params(self):
    
  1000.         field = SimpleArrayField(forms.CharField(max_length=2))
    
  1001.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1002.             field.clean("abc,c,defg")
    
  1003.         errors = cm.exception.error_list
    
  1004.         self.assertEqual(len(errors), 2)
    
  1005.         first_error = errors[0]
    
  1006.         self.assertEqual(
    
  1007.             first_error.message,
    
  1008.             "Item 1 in the array did not validate: Ensure this value has at most 2 "
    
  1009.             "characters (it has 3).",
    
  1010.         )
    
  1011.         self.assertEqual(first_error.code, "item_invalid")
    
  1012.         self.assertEqual(
    
  1013.             first_error.params,
    
  1014.             {"nth": 1, "value": "abc", "limit_value": 2, "show_value": 3},
    
  1015.         )
    
  1016.         second_error = errors[1]
    
  1017.         self.assertEqual(
    
  1018.             second_error.message,
    
  1019.             "Item 3 in the array did not validate: Ensure this value has at most 2 "
    
  1020.             "characters (it has 4).",
    
  1021.         )
    
  1022.         self.assertEqual(second_error.code, "item_invalid")
    
  1023.         self.assertEqual(
    
  1024.             second_error.params,
    
  1025.             {"nth": 3, "value": "defg", "limit_value": 2, "show_value": 4},
    
  1026.         )
    
  1027. 
    
  1028.     def test_validators_fail(self):
    
  1029.         field = SimpleArrayField(forms.RegexField("[a-e]{2}"))
    
  1030.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1031.             field.clean("a,bc,de")
    
  1032.         self.assertEqual(
    
  1033.             cm.exception.messages[0],
    
  1034.             "Item 1 in the array did not validate: Enter a valid value.",
    
  1035.         )
    
  1036. 
    
  1037.     def test_delimiter(self):
    
  1038.         field = SimpleArrayField(forms.CharField(), delimiter="|")
    
  1039.         value = field.clean("a|b|c")
    
  1040.         self.assertEqual(value, ["a", "b", "c"])
    
  1041. 
    
  1042.     def test_delimiter_with_nesting(self):
    
  1043.         field = SimpleArrayField(SimpleArrayField(forms.CharField()), delimiter="|")
    
  1044.         value = field.clean("a,b|c,d")
    
  1045.         self.assertEqual(value, [["a", "b"], ["c", "d"]])
    
  1046. 
    
  1047.     def test_prepare_value(self):
    
  1048.         field = SimpleArrayField(forms.CharField())
    
  1049.         value = field.prepare_value(["a", "b", "c"])
    
  1050.         self.assertEqual(value, "a,b,c")
    
  1051. 
    
  1052.     def test_max_length(self):
    
  1053.         field = SimpleArrayField(forms.CharField(), max_length=2)
    
  1054.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1055.             field.clean("a,b,c")
    
  1056.         self.assertEqual(
    
  1057.             cm.exception.messages[0],
    
  1058.             "List contains 3 items, it should contain no more than 2.",
    
  1059.         )
    
  1060. 
    
  1061.     def test_min_length(self):
    
  1062.         field = SimpleArrayField(forms.CharField(), min_length=4)
    
  1063.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1064.             field.clean("a,b,c")
    
  1065.         self.assertEqual(
    
  1066.             cm.exception.messages[0],
    
  1067.             "List contains 3 items, it should contain no fewer than 4.",
    
  1068.         )
    
  1069. 
    
  1070.     def test_required(self):
    
  1071.         field = SimpleArrayField(forms.CharField(), required=True)
    
  1072.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1073.             field.clean("")
    
  1074.         self.assertEqual(cm.exception.messages[0], "This field is required.")
    
  1075. 
    
  1076.     def test_model_field_formfield(self):
    
  1077.         model_field = ArrayField(models.CharField(max_length=27))
    
  1078.         form_field = model_field.formfield()
    
  1079.         self.assertIsInstance(form_field, SimpleArrayField)
    
  1080.         self.assertIsInstance(form_field.base_field, forms.CharField)
    
  1081.         self.assertEqual(form_field.base_field.max_length, 27)
    
  1082. 
    
  1083.     def test_model_field_formfield_size(self):
    
  1084.         model_field = ArrayField(models.CharField(max_length=27), size=4)
    
  1085.         form_field = model_field.formfield()
    
  1086.         self.assertIsInstance(form_field, SimpleArrayField)
    
  1087.         self.assertEqual(form_field.max_length, 4)
    
  1088. 
    
  1089.     def test_model_field_choices(self):
    
  1090.         model_field = ArrayField(models.IntegerField(choices=((1, "A"), (2, "B"))))
    
  1091.         form_field = model_field.formfield()
    
  1092.         self.assertEqual(form_field.clean("1,2"), [1, 2])
    
  1093. 
    
  1094.     def test_already_converted_value(self):
    
  1095.         field = SimpleArrayField(forms.CharField())
    
  1096.         vals = ["a", "b", "c"]
    
  1097.         self.assertEqual(field.clean(vals), vals)
    
  1098. 
    
  1099.     def test_has_changed(self):
    
  1100.         field = SimpleArrayField(forms.IntegerField())
    
  1101.         self.assertIs(field.has_changed([1, 2], [1, 2]), False)
    
  1102.         self.assertIs(field.has_changed([1, 2], "1,2"), False)
    
  1103.         self.assertIs(field.has_changed([1, 2], "1,2,3"), True)
    
  1104.         self.assertIs(field.has_changed([1, 2], "a,b"), True)
    
  1105. 
    
  1106.     def test_has_changed_empty(self):
    
  1107.         field = SimpleArrayField(forms.CharField())
    
  1108.         self.assertIs(field.has_changed(None, None), False)
    
  1109.         self.assertIs(field.has_changed(None, ""), False)
    
  1110.         self.assertIs(field.has_changed(None, []), False)
    
  1111.         self.assertIs(field.has_changed([], None), False)
    
  1112.         self.assertIs(field.has_changed([], ""), False)
    
  1113. 
    
  1114. 
    
  1115. class TestSplitFormField(PostgreSQLSimpleTestCase):
    
  1116.     def test_valid(self):
    
  1117.         class SplitForm(forms.Form):
    
  1118.             array = SplitArrayField(forms.CharField(), size=3)
    
  1119. 
    
  1120.         data = {"array_0": "a", "array_1": "b", "array_2": "c"}
    
  1121.         form = SplitForm(data)
    
  1122.         self.assertTrue(form.is_valid())
    
  1123.         self.assertEqual(form.cleaned_data, {"array": ["a", "b", "c"]})
    
  1124. 
    
  1125.     def test_required(self):
    
  1126.         class SplitForm(forms.Form):
    
  1127.             array = SplitArrayField(forms.CharField(), required=True, size=3)
    
  1128. 
    
  1129.         data = {"array_0": "", "array_1": "", "array_2": ""}
    
  1130.         form = SplitForm(data)
    
  1131.         self.assertFalse(form.is_valid())
    
  1132.         self.assertEqual(form.errors, {"array": ["This field is required."]})
    
  1133. 
    
  1134.     def test_remove_trailing_nulls(self):
    
  1135.         class SplitForm(forms.Form):
    
  1136.             array = SplitArrayField(
    
  1137.                 forms.CharField(required=False), size=5, remove_trailing_nulls=True
    
  1138.             )
    
  1139. 
    
  1140.         data = {
    
  1141.             "array_0": "a",
    
  1142.             "array_1": "",
    
  1143.             "array_2": "b",
    
  1144.             "array_3": "",
    
  1145.             "array_4": "",
    
  1146.         }
    
  1147.         form = SplitForm(data)
    
  1148.         self.assertTrue(form.is_valid(), form.errors)
    
  1149.         self.assertEqual(form.cleaned_data, {"array": ["a", "", "b"]})
    
  1150. 
    
  1151.     def test_remove_trailing_nulls_not_required(self):
    
  1152.         class SplitForm(forms.Form):
    
  1153.             array = SplitArrayField(
    
  1154.                 forms.CharField(required=False),
    
  1155.                 size=2,
    
  1156.                 remove_trailing_nulls=True,
    
  1157.                 required=False,
    
  1158.             )
    
  1159. 
    
  1160.         data = {"array_0": "", "array_1": ""}
    
  1161.         form = SplitForm(data)
    
  1162.         self.assertTrue(form.is_valid())
    
  1163.         self.assertEqual(form.cleaned_data, {"array": []})
    
  1164. 
    
  1165.     def test_required_field(self):
    
  1166.         class SplitForm(forms.Form):
    
  1167.             array = SplitArrayField(forms.CharField(), size=3)
    
  1168. 
    
  1169.         data = {"array_0": "a", "array_1": "b", "array_2": ""}
    
  1170.         form = SplitForm(data)
    
  1171.         self.assertFalse(form.is_valid())
    
  1172.         self.assertEqual(
    
  1173.             form.errors,
    
  1174.             {
    
  1175.                 "array": [
    
  1176.                     "Item 3 in the array did not validate: This field is required."
    
  1177.                 ]
    
  1178.             },
    
  1179.         )
    
  1180. 
    
  1181.     def test_invalid_integer(self):
    
  1182.         msg = (
    
  1183.             "Item 2 in the array did not validate: Ensure this value is less than or "
    
  1184.             "equal to 100."
    
  1185.         )
    
  1186.         with self.assertRaisesMessage(exceptions.ValidationError, msg):
    
  1187.             SplitArrayField(forms.IntegerField(max_value=100), size=2).clean([0, 101])
    
  1188. 
    
  1189.     # To locate the widget's template.
    
  1190.     @modify_settings(INSTALLED_APPS={"append": "django.contrib.postgres"})
    
  1191.     def test_rendering(self):
    
  1192.         class SplitForm(forms.Form):
    
  1193.             array = SplitArrayField(forms.CharField(), size=3)
    
  1194. 
    
  1195.         self.assertHTMLEqual(
    
  1196.             str(SplitForm()),
    
  1197.             """
    
  1198.             <div>
    
  1199.                 <label for="id_array_0">Array:</label>
    
  1200.                 <input id="id_array_0" name="array_0" type="text" required>
    
  1201.                 <input id="id_array_1" name="array_1" type="text" required>
    
  1202.                 <input id="id_array_2" name="array_2" type="text" required>
    
  1203.             </div>
    
  1204.         """,
    
  1205.         )
    
  1206. 
    
  1207.     def test_invalid_char_length(self):
    
  1208.         field = SplitArrayField(forms.CharField(max_length=2), size=3)
    
  1209.         with self.assertRaises(exceptions.ValidationError) as cm:
    
  1210.             field.clean(["abc", "c", "defg"])
    
  1211.         self.assertEqual(
    
  1212.             cm.exception.messages,
    
  1213.             [
    
  1214.                 "Item 1 in the array did not validate: Ensure this value has at most 2 "
    
  1215.                 "characters (it has 3).",
    
  1216.                 "Item 3 in the array did not validate: Ensure this value has at most 2 "
    
  1217.                 "characters (it has 4).",
    
  1218.             ],
    
  1219.         )
    
  1220. 
    
  1221.     def test_splitarraywidget_value_omitted_from_data(self):
    
  1222.         class Form(forms.ModelForm):
    
  1223.             field = SplitArrayField(forms.IntegerField(), required=False, size=2)
    
  1224. 
    
  1225.             class Meta:
    
  1226.                 model = IntegerArrayModel
    
  1227.                 fields = ("field",)
    
  1228. 
    
  1229.         form = Form({"field_0": "1", "field_1": "2"})
    
  1230.         self.assertEqual(form.errors, {})
    
  1231.         obj = form.save(commit=False)
    
  1232.         self.assertEqual(obj.field, [1, 2])
    
  1233. 
    
  1234.     def test_splitarrayfield_has_changed(self):
    
  1235.         class Form(forms.ModelForm):
    
  1236.             field = SplitArrayField(forms.IntegerField(), required=False, size=2)
    
  1237. 
    
  1238.             class Meta:
    
  1239.                 model = IntegerArrayModel
    
  1240.                 fields = ("field",)
    
  1241. 
    
  1242.         tests = [
    
  1243.             ({}, {"field_0": "", "field_1": ""}, True),
    
  1244.             ({"field": None}, {"field_0": "", "field_1": ""}, True),
    
  1245.             ({"field": [1]}, {"field_0": "", "field_1": ""}, True),
    
  1246.             ({"field": [1]}, {"field_0": "1", "field_1": "0"}, True),
    
  1247.             ({"field": [1, 2]}, {"field_0": "1", "field_1": "2"}, False),
    
  1248.             ({"field": [1, 2]}, {"field_0": "a", "field_1": "b"}, True),
    
  1249.         ]
    
  1250.         for initial, data, expected_result in tests:
    
  1251.             with self.subTest(initial=initial, data=data):
    
  1252.                 obj = IntegerArrayModel(**initial)
    
  1253.                 form = Form(data, instance=obj)
    
  1254.                 self.assertIs(form.has_changed(), expected_result)
    
  1255. 
    
  1256.     def test_splitarrayfield_remove_trailing_nulls_has_changed(self):
    
  1257.         class Form(forms.ModelForm):
    
  1258.             field = SplitArrayField(
    
  1259.                 forms.IntegerField(), required=False, size=2, remove_trailing_nulls=True
    
  1260.             )
    
  1261. 
    
  1262.             class Meta:
    
  1263.                 model = IntegerArrayModel
    
  1264.                 fields = ("field",)
    
  1265. 
    
  1266.         tests = [
    
  1267.             ({}, {"field_0": "", "field_1": ""}, False),
    
  1268.             ({"field": None}, {"field_0": "", "field_1": ""}, False),
    
  1269.             ({"field": []}, {"field_0": "", "field_1": ""}, False),
    
  1270.             ({"field": [1]}, {"field_0": "1", "field_1": ""}, False),
    
  1271.         ]
    
  1272.         for initial, data, expected_result in tests:
    
  1273.             with self.subTest(initial=initial, data=data):
    
  1274.                 obj = IntegerArrayModel(**initial)
    
  1275.                 form = Form(data, instance=obj)
    
  1276.                 self.assertIs(form.has_changed(), expected_result)
    
  1277. 
    
  1278. 
    
  1279. class TestSplitFormWidget(PostgreSQLWidgetTestCase):
    
  1280.     def test_get_context(self):
    
  1281.         self.assertEqual(
    
  1282.             SplitArrayWidget(forms.TextInput(), size=2).get_context(
    
  1283.                 "name", ["val1", "val2"]
    
  1284.             ),
    
  1285.             {
    
  1286.                 "widget": {
    
  1287.                     "name": "name",
    
  1288.                     "is_hidden": False,
    
  1289.                     "required": False,
    
  1290.                     "value": "['val1', 'val2']",
    
  1291.                     "attrs": {},
    
  1292.                     "template_name": "postgres/widgets/split_array.html",
    
  1293.                     "subwidgets": [
    
  1294.                         {
    
  1295.                             "name": "name_0",
    
  1296.                             "is_hidden": False,
    
  1297.                             "required": False,
    
  1298.                             "value": "val1",
    
  1299.                             "attrs": {},
    
  1300.                             "template_name": "django/forms/widgets/text.html",
    
  1301.                             "type": "text",
    
  1302.                         },
    
  1303.                         {
    
  1304.                             "name": "name_1",
    
  1305.                             "is_hidden": False,
    
  1306.                             "required": False,
    
  1307.                             "value": "val2",
    
  1308.                             "attrs": {},
    
  1309.                             "template_name": "django/forms/widgets/text.html",
    
  1310.                             "type": "text",
    
  1311.                         },
    
  1312.                     ],
    
  1313.                 }
    
  1314.             },
    
  1315.         )
    
  1316. 
    
  1317.     def test_checkbox_get_context_attrs(self):
    
  1318.         context = SplitArrayWidget(
    
  1319.             forms.CheckboxInput(),
    
  1320.             size=2,
    
  1321.         ).get_context("name", [True, False])
    
  1322.         self.assertEqual(context["widget"]["value"], "[True, False]")
    
  1323.         self.assertEqual(
    
  1324.             [subwidget["attrs"] for subwidget in context["widget"]["subwidgets"]],
    
  1325.             [{"checked": True}, {}],
    
  1326.         )
    
  1327. 
    
  1328.     def test_render(self):
    
  1329.         self.check_html(
    
  1330.             SplitArrayWidget(forms.TextInput(), size=2),
    
  1331.             "array",
    
  1332.             None,
    
  1333.             """
    
  1334.             <input name="array_0" type="text">
    
  1335.             <input name="array_1" type="text">
    
  1336.             """,
    
  1337.         )
    
  1338. 
    
  1339.     def test_render_attrs(self):
    
  1340.         self.check_html(
    
  1341.             SplitArrayWidget(forms.TextInput(), size=2),
    
  1342.             "array",
    
  1343.             ["val1", "val2"],
    
  1344.             attrs={"id": "foo"},
    
  1345.             html=(
    
  1346.                 """
    
  1347.                 <input id="foo_0" name="array_0" type="text" value="val1">
    
  1348.                 <input id="foo_1" name="array_1" type="text" value="val2">
    
  1349.                 """
    
  1350.             ),
    
  1351.         )
    
  1352. 
    
  1353.     def test_value_omitted_from_data(self):
    
  1354.         widget = SplitArrayWidget(forms.TextInput(), size=2)
    
  1355.         self.assertIs(widget.value_omitted_from_data({}, {}, "field"), True)
    
  1356.         self.assertIs(
    
  1357.             widget.value_omitted_from_data({"field_0": "value"}, {}, "field"), False
    
  1358.         )
    
  1359.         self.assertIs(
    
  1360.             widget.value_omitted_from_data({"field_1": "value"}, {}, "field"), False
    
  1361.         )
    
  1362.         self.assertIs(
    
  1363.             widget.value_omitted_from_data(
    
  1364.                 {"field_0": "value", "field_1": "value"}, {}, "field"
    
  1365.             ),
    
  1366.             False,
    
  1367.         )