mirror of
https://github.com/django/django.git
synced 2025-03-06 15:32:33 +00:00
Fixed #36086 -- Fixed crash when using GeneratedField with non-AutoField pk.
The previous logic was systematically attempting to retrieve last_insert_id even for models without an AutoField primary key when they had a GeneratedField on backends that can't return columns from INSERT. The issue affected MySQL, SQLite < 3.35, and Oracle when the use_returning_into option was disabled and could result in either crashes when the non-auto primary key wasn't an IntegerField subclass or silent misassignment of bogus insert ids (0 or the previous auto primary key insert value) to the first defined generated field value.
This commit is contained in:
parent
20eb4bca7d
commit
9e55201555
@ -8,7 +8,7 @@ from django.core.exceptions import EmptyResultSet, FieldError, FullResultSet
|
||||
from django.db import DatabaseError, NotSupportedError
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.db.models.expressions import ColPairs, F, OrderBy, RawSQL, Ref, Value
|
||||
from django.db.models.fields import composite
|
||||
from django.db.models.fields import AutoField, composite
|
||||
from django.db.models.functions import Cast, Random
|
||||
from django.db.models.lookups import Lookup
|
||||
from django.db.models.query_utils import select_related_descend
|
||||
@ -1910,17 +1910,23 @@ class SQLInsertCompiler(SQLCompiler):
|
||||
)
|
||||
]
|
||||
cols = [field.get_col(opts.db_table) for field in self.returning_fields]
|
||||
else:
|
||||
cols = [opts.pk.get_col(opts.db_table)]
|
||||
elif returning_fields and isinstance(
|
||||
returning_field := returning_fields[0], AutoField
|
||||
):
|
||||
cols = [returning_field.get_col(opts.db_table)]
|
||||
rows = [
|
||||
(
|
||||
self.connection.ops.last_insert_id(
|
||||
cursor,
|
||||
opts.db_table,
|
||||
opts.pk.column,
|
||||
returning_field.column,
|
||||
),
|
||||
)
|
||||
]
|
||||
else:
|
||||
# Backend doesn't support returning fields and no auto-field
|
||||
# that can be retrieved from `last_insert_id` was specified.
|
||||
return []
|
||||
converters = self.get_converters(cols)
|
||||
if converters:
|
||||
rows = self.apply_converters(rows, converters)
|
||||
|
@ -527,6 +527,19 @@ class GeneratedModel(models.Model):
|
||||
required_db_features = {"supports_stored_generated_columns"}
|
||||
|
||||
|
||||
class GeneratedModelNonAutoPk(models.Model):
|
||||
id = models.IntegerField(primary_key=True)
|
||||
a = models.IntegerField()
|
||||
b = models.GeneratedField(
|
||||
expression=F("a"),
|
||||
output_field=models.IntegerField(),
|
||||
db_persist=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
required_db_features = {"supports_stored_generated_columns"}
|
||||
|
||||
|
||||
class GeneratedModelVirtual(models.Model):
|
||||
a = models.IntegerField()
|
||||
b = models.IntegerField()
|
||||
|
@ -22,6 +22,7 @@ from .models import (
|
||||
GeneratedModelCheckConstraint,
|
||||
GeneratedModelCheckConstraintVirtual,
|
||||
GeneratedModelFieldWithConverters,
|
||||
GeneratedModelNonAutoPk,
|
||||
GeneratedModelNull,
|
||||
GeneratedModelNullVirtual,
|
||||
GeneratedModelOutputFieldDbCollation,
|
||||
@ -356,6 +357,12 @@ class StoredGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):
|
||||
obj = self._refresh_if_needed(obj)
|
||||
self.assertEqual(obj.field, obj.field_copy)
|
||||
|
||||
def test_create_with_non_auto_pk(self):
|
||||
obj = GeneratedModelNonAutoPk.objects.create(id=1, a=2)
|
||||
self.assertEqual(obj.id, 1)
|
||||
self.assertEqual(obj.a, 2)
|
||||
self.assertEqual(obj.b, 2)
|
||||
|
||||
|
||||
@skipUnlessDBFeature("supports_virtual_generated_columns")
|
||||
class VirtualGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):
|
||||
|
Loading…
x
Reference in New Issue
Block a user