mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #35024 -- Fixed model instance creation crash on GeneratedField.output_field with backend converters.
Regression in d9de74141e.
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.
			
			
This commit is contained in:
		| @@ -1819,6 +1819,7 @@ class SQLInsertCompiler(SQLCompiler): | |||||||
|         ) |         ) | ||||||
|         opts = self.query.get_meta() |         opts = self.query.get_meta() | ||||||
|         self.returning_fields = returning_fields |         self.returning_fields = returning_fields | ||||||
|  |         cols = [] | ||||||
|         with self.connection.cursor() as cursor: |         with self.connection.cursor() as cursor: | ||||||
|             for sql, params in self.as_sql(): |             for sql, params in self.as_sql(): | ||||||
|                 cursor.execute(sql, params) |                 cursor.execute(sql, params) | ||||||
| @@ -1829,6 +1830,7 @@ class SQLInsertCompiler(SQLCompiler): | |||||||
|                 and len(self.query.objs) > 1 |                 and len(self.query.objs) > 1 | ||||||
|             ): |             ): | ||||||
|                 rows = self.connection.ops.fetch_returned_insert_rows(cursor) |                 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: |             elif self.connection.features.can_return_columns_from_insert: | ||||||
|                 assert len(self.query.objs) == 1 |                 assert len(self.query.objs) == 1 | ||||||
|                 rows = [ |                 rows = [ | ||||||
| @@ -1837,7 +1839,9 @@ class SQLInsertCompiler(SQLCompiler): | |||||||
|                         self.returning_params, |                         self.returning_params, | ||||||
|                     ) |                     ) | ||||||
|                 ] |                 ] | ||||||
|  |                 cols = [field.get_col(opts.db_table) for field in self.returning_fields] | ||||||
|             else: |             else: | ||||||
|  |                 cols = [opts.pk.get_col(opts.db_table)] | ||||||
|                 rows = [ |                 rows = [ | ||||||
|                     ( |                     ( | ||||||
|                         self.connection.ops.last_insert_id( |                         self.connection.ops.last_insert_id( | ||||||
| @@ -1847,7 +1851,6 @@ class SQLInsertCompiler(SQLCompiler): | |||||||
|                         ), |                         ), | ||||||
|                     ) |                     ) | ||||||
|                 ] |                 ] | ||||||
|         cols = [field.get_col(opts.db_table) for field in self.returning_fields] |  | ||||||
|         converters = self.get_converters(cols) |         converters = self.get_converters(cols) | ||||||
|         if converters: |         if converters: | ||||||
|             rows = list(self.apply_converters(rows, converters)) |             rows = list(self.apply_converters(rows, converters)) | ||||||
|   | |||||||
| @@ -12,3 +12,7 @@ Bugfixes | |||||||
| * Reallowed, following a regression in Django 5.0, using a foreign key to a | * Reallowed, following a regression in Django 5.0, using a foreign key to a | ||||||
|   model with a primary key that is not ``AutoField`` in |   model with a primary key that is not ``AutoField`` in | ||||||
|   :attr:`.ModelAdmin.list_filter` (:ticket:`35020`). |   :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`). | ||||||
|   | |||||||
| @@ -482,6 +482,18 @@ class UUIDGrandchild(UUIDChild): | |||||||
|     pass |     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): | class GeneratedModel(models.Model): | ||||||
|     a = models.IntegerField() |     a = models.IntegerField() | ||||||
|     b = models.IntegerField() |     b = models.IntegerField() | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import uuid | ||||||
|  |  | ||||||
| from django.apps import apps | from django.apps import apps | ||||||
| from django.db import IntegrityError, connection | from django.db import IntegrityError, connection | ||||||
| from django.db.models import ( | from django.db.models import ( | ||||||
| @@ -14,6 +16,7 @@ from django.test.utils import isolate_apps | |||||||
|  |  | ||||||
| from .models import ( | from .models import ( | ||||||
|     GeneratedModel, |     GeneratedModel, | ||||||
|  |     GeneratedModelFieldWithConverters, | ||||||
|     GeneratedModelNull, |     GeneratedModelNull, | ||||||
|     GeneratedModelNullVirtual, |     GeneratedModelNullVirtual, | ||||||
|     GeneratedModelOutputFieldDbCollation, |     GeneratedModelOutputFieldDbCollation, | ||||||
| @@ -266,6 +269,11 @@ class StoredGeneratedFieldTests(GeneratedFieldTestMixin, TestCase): | |||||||
|     output_field_db_collation_model = GeneratedModelOutputFieldDbCollation |     output_field_db_collation_model = GeneratedModelOutputFieldDbCollation | ||||||
|     params_model = GeneratedModelParams |     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") | @skipUnlessDBFeature("supports_virtual_generated_columns") | ||||||
| class VirtualGeneratedFieldTests(GeneratedFieldTestMixin, TestCase): | class VirtualGeneratedFieldTests(GeneratedFieldTestMixin, TestCase): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user