summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wesseling <chris@maykinmedia.nl>2025-11-20 15:01:14 +0100
committerJacob Walls <jacobtylerwalls@gmail.com>2025-11-20 17:24:10 -0500
commitac9bdcabe10fb7ac0c7e9ebcd879f5e34bee776f (patch)
tree517bd72a81b32a1e0664a0b9a22cdd2c56a67356
parent001c2f546b4053acb04f16d6b704f7b4fbca1c45 (diff)
[5.2.x] Fixed #36748 -- Filtered non-standard placeholders from UNNEST queries.
Backport of 5834643f43a767fe19f2c6d10217b204e7584ec8 from main.
-rw-r--r--django/db/backends/postgresql/compiler.py5
-rw-r--r--docs/releases/5.2.9.txt3
-rw-r--r--tests/postgres_tests/fields.py5
-rw-r--r--tests/postgres_tests/migrations/0002_create_test_models.py23
-rw-r--r--tests/postgres_tests/models.py5
-rw-r--r--tests/postgres_tests/test_bulk_update.py8
6 files changed, 49 insertions, 0 deletions
diff --git a/django/db/backends/postgresql/compiler.py b/django/db/backends/postgresql/compiler.py
index 344773fb7a..dc2db148ae 100644
--- a/django/db/backends/postgresql/compiler.py
+++ b/django/db/backends/postgresql/compiler.py
@@ -36,6 +36,11 @@ class SQLInsertCompiler(BaseSQLInsertCompiler):
# Lack of fields denote the usage of the DEFAULT keyword
# for the insertion of empty rows.
or any(field is None for field in fields)
+ # Field.get_placeholder takes value as an argument, so the
+ # resulting placeholder might be dependent on the value.
+ # in UNNEST requires a single placeholder to "fit all values" in
+ # the array.
+ or any(hasattr(field, "get_placeholder") for field in fields)
# Fields that don't use standard internal types might not be
# unnest'able (e.g. array and geometry types are known to be
# problematic).
diff --git a/docs/releases/5.2.9.txt b/docs/releases/5.2.9.txt
index 0d726de640..588c278be5 100644
--- a/docs/releases/5.2.9.txt
+++ b/docs/releases/5.2.9.txt
@@ -13,3 +13,6 @@ Bugfixes
``django.utils.feedgenerator.Stylesheet.__str__()`` did not escape
the ``url``, ``mimetype``, and ``media`` attributes, potentially leading
to invalid XML markup (:ticket:`36733`).
+
+* Fixed a bug in Django 5.2 on PostgreSQL where ``bulk_create()`` did not apply
+ a field's custom query placeholders (:ticket:`36748`).
diff --git a/tests/postgres_tests/fields.py b/tests/postgres_tests/fields.py
index c5dddf197f..d099effdd5 100644
--- a/tests/postgres_tests/fields.py
+++ b/tests/postgres_tests/fields.py
@@ -57,3 +57,8 @@ except ImportError:
class EnumField(models.CharField):
def get_prep_value(self, value):
return value.value if isinstance(value, enum.Enum) else value
+
+
+class OffByOneField(models.IntegerField):
+ def get_placeholder(self, value, compiler, connection):
+ return "(%s + 1)"
diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py
index 31705ae21a..1aa900f07a 100644
--- a/tests/postgres_tests/migrations/0002_create_test_models.py
+++ b/tests/postgres_tests/migrations/0002_create_test_models.py
@@ -9,6 +9,7 @@ from ..fields import (
EnumField,
HStoreField,
IntegerRangeField,
+ OffByOneField,
SearchVectorField,
)
from ..models import TagField
@@ -570,4 +571,26 @@ class Migration(migrations.Migration):
"required_db_vendor": "postgresql",
},
),
+ migrations.CreateModel(
+ name="OffByOneModel",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ verbose_name="ID",
+ serialize=False,
+ auto_created=True,
+ primary_key=True,
+ ),
+ ),
+ (
+ "one_off",
+ OffByOneField(),
+ ),
+ ],
+ options={
+ "required_db_vendor": "postgresql",
+ },
+ bases=(models.Model,),
+ ),
]
diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py
index 1563f6a35d..7c606a83a9 100644
--- a/tests/postgres_tests/models.py
+++ b/tests/postgres_tests/models.py
@@ -9,6 +9,7 @@ from .fields import (
EnumField,
HStoreField,
IntegerRangeField,
+ OffByOneField,
SearchVectorField,
)
@@ -207,3 +208,7 @@ class HotelReservation(PostgreSQLModel):
end = models.DateTimeField()
cancelled = models.BooleanField(default=False)
requirements = models.JSONField(blank=True, null=True)
+
+
+class OffByOneModel(PostgreSQLModel):
+ one_off = OffByOneField()
diff --git a/tests/postgres_tests/test_bulk_update.py b/tests/postgres_tests/test_bulk_update.py
index 85dfcedd09..f3e8ac8a40 100644
--- a/tests/postgres_tests/test_bulk_update.py
+++ b/tests/postgres_tests/test_bulk_update.py
@@ -6,6 +6,7 @@ from .models import (
IntegerArrayModel,
NestedIntegerArrayModel,
NullableIntegerArrayModel,
+ OffByOneModel,
OtherTypesArrayModel,
RangesModel,
)
@@ -44,3 +45,10 @@ class BulkSaveTests(PostgreSQLTestCase):
self.assertSequenceEqual(
Model.objects.filter(**{field: new}), instances
)
+
+ def test_bulk_create(self):
+ OffByOneModel.objects.bulk_create(OffByOneModel(one_off=0) for _ in range(20))
+
+ self.assertSequenceEqual(
+ [m.one_off for m in OffByOneModel.objects.all()], 20 * [1]
+ )