浏览代码

[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
Mariusz Felisiak 1 年之前
父节点
当前提交
415a08a528

+ 4 - 1
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))

+ 4 - 0
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`).

+ 12 - 0
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()

+ 8 - 0
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):