summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2023-12-07 20:50:18 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-12-07 20:50:48 +0100
commit415a08a5283a0f76408ec23c6f6af4a0002d7651 (patch)
tree01332c74cfabed7cb5695fc5300007a8383e3e45
parent454fd50efb4b6bba54abdbcc01507190c780382f (diff)
[5.0.x] Fixed #35024 -- Fixed model instance creation crash on GeneratedField.output_field with backend converters.
Regression in d9de74141e8a920940f1b91ed0a3ccb835b55729. This is a long standing issue, however it caused a crash of GeneratedFields for all output fields that have backend-specific converters when the RETURNING clause is not supported (MySQL and SQLite < 3.35). That's why severity was exacerbated. Backport of 5b3b791e9046461901df3898be8544e14d91b931 from main
-rw-r--r--django/db/models/sql/compiler.py5
-rw-r--r--docs/releases/5.0.1.txt4
-rw-r--r--tests/model_fields/models.py12
-rw-r--r--tests/model_fields/test_generatedfield.py8
4 files changed, 28 insertions, 1 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index b28dc925ba..5d2e7fd85c 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -1817,6 +1817,7 @@ class SQLInsertCompiler(SQLCompiler):
)
opts = self.query.get_meta()
self.returning_fields = returning_fields
+ cols = []
with self.connection.cursor() as cursor:
for sql, params in self.as_sql():
cursor.execute(sql, params)
@@ -1827,6 +1828,7 @@ class SQLInsertCompiler(SQLCompiler):
and len(self.query.objs) > 1
):
rows = self.connection.ops.fetch_returned_insert_rows(cursor)
+ cols = [field.get_col(opts.db_table) for field in self.returning_fields]
elif self.connection.features.can_return_columns_from_insert:
assert len(self.query.objs) == 1
rows = [
@@ -1835,7 +1837,9 @@ class SQLInsertCompiler(SQLCompiler):
self.returning_params,
)
]
+ cols = [field.get_col(opts.db_table) for field in self.returning_fields]
else:
+ cols = [opts.pk.get_col(opts.db_table)]
rows = [
(
self.connection.ops.last_insert_id(
@@ -1845,7 +1849,6 @@ class SQLInsertCompiler(SQLCompiler):
),
)
]
- cols = [field.get_col(opts.db_table) for field in self.returning_fields]
converters = self.get_converters(cols)
if converters:
rows = list(self.apply_converters(rows, converters))
diff --git a/docs/releases/5.0.1.txt b/docs/releases/5.0.1.txt
index 871f55ec6d..a8b886c6e2 100644
--- a/docs/releases/5.0.1.txt
+++ b/docs/releases/5.0.1.txt
@@ -12,3 +12,7 @@ Bugfixes
* Reallowed, following a regression in Django 5.0, using a foreign key to a
model with a primary key that is not ``AutoField`` in
:attr:`.ModelAdmin.list_filter` (:ticket:`35020`).
+
+* Fixed a long standing bug in handling the ``RETURNING INTO`` clause that
+ caused a crash when creating a model instance with a ``GeneratedField`` which
+ ``output_field`` had backend-specific converters (:ticket:`35024`).
diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py
index b4b7b5bd4c..aea02964e2 100644
--- a/tests/model_fields/models.py
+++ b/tests/model_fields/models.py
@@ -482,6 +482,18 @@ class UUIDGrandchild(UUIDChild):
pass
+class GeneratedModelFieldWithConverters(models.Model):
+ field = models.UUIDField()
+ field_copy = models.GeneratedField(
+ expression=F("field"),
+ output_field=models.UUIDField(),
+ db_persist=True,
+ )
+
+ class Meta:
+ required_db_features = {"supports_stored_generated_columns"}
+
+
class GeneratedModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py
index 9e5d9d87c3..056a80c294 100644
--- a/tests/model_fields/test_generatedfield.py
+++ b/tests/model_fields/test_generatedfield.py
@@ -1,3 +1,5 @@
+import uuid
+
from django.apps import apps
from django.db import IntegrityError, connection
from django.db.models import (
@@ -14,6 +16,7 @@ from django.test.utils import isolate_apps
from .models import (
GeneratedModel,
+ GeneratedModelFieldWithConverters,
GeneratedModelNull,
GeneratedModelNullVirtual,
GeneratedModelOutputFieldDbCollation,
@@ -266,6 +269,11 @@ class StoredGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):
output_field_db_collation_model = GeneratedModelOutputFieldDbCollation
params_model = GeneratedModelParams
+ def test_create_field_with_db_converters(self):
+ obj = GeneratedModelFieldWithConverters.objects.create(field=uuid.uuid4())
+ obj = self._refresh_if_needed(obj)
+ self.assertEqual(obj.field, obj.field_copy)
+
@skipUnlessDBFeature("supports_virtual_generated_columns")
class VirtualGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):