1
0
mirror of https://github.com/django/django.git synced 2025-04-05 22:16:41 +00:00

Fixed #36260 -- Made bulk_create() work with DB-generated primary keys.

Co-authored-by: Simon Charette <charette.s@gmail.com>
This commit is contained in:
Dmitry Shachnev 2025-03-16 20:50:45 +03:00 committed by Sarah Boyce
parent c75fbe8430
commit 77b4ecbd53
4 changed files with 21 additions and 6 deletions

View File

@ -23,7 +23,7 @@ from django.db import (
from django.db.models import AutoField, DateField, DateTimeField, Field, sql
from django.db.models.constants import LOOKUP_SEP, OnConflict
from django.db.models.deletion import Collector
from django.db.models.expressions import Case, F, Value, When
from django.db.models.expressions import Case, DatabaseDefault, F, Value, When
from django.db.models.functions import Cast, Trunc
from django.db.models.query_utils import FilteredRelation, Q
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, ROW_COUNT
@ -789,7 +789,10 @@ class QuerySet(AltersData):
objs = list(objs)
self._prepare_for_bulk_create(objs)
with transaction.atomic(using=self.db, savepoint=False):
objs_without_pk, objs_with_pk = partition(lambda o: o._is_pk_set(), objs)
objs_without_pk, objs_with_pk = partition(
lambda o: o._is_pk_set() and not isinstance(o.pk, DatabaseDefault),
objs,
)
if objs_with_pk:
returned_columns = self._batched_insert(
objs_with_pk,

View File

@ -2427,10 +2427,11 @@ This has a number of caveats though:
* The model's ``save()`` method will not be called, and the ``pre_save`` and
``post_save`` signals will not be sent.
* It does not work with child models in a multi-table inheritance scenario.
* If the model's primary key is an :class:`~django.db.models.AutoField` and
``ignore_conflicts`` is False, the primary key attribute can only be
retrieved on certain databases (currently PostgreSQL, MariaDB, and SQLite
3.35+). On other databases, it will not be set.
* If the model's primary key is an :class:`~django.db.models.AutoField` or has
a :attr:`~django.db.models.Field.db_default` value, and ``ignore_conflicts``
is ``False``, the primary key attribute can only be retrieved on certain
databases (currently PostgreSQL, MariaDB, and SQLite 3.35+). On other
databases, it will not be set.
* It does not work with many-to-many relationships.
* It casts ``objs`` to a list, which fully evaluates ``objs`` if it's a
generator. The cast allows inspecting all objects so that any objects with a

View File

@ -147,3 +147,7 @@ class RelatedModel(models.Model):
class DbDefaultModel(models.Model):
name = models.CharField(max_length=10)
created_at = models.DateTimeField(db_default=Now())
class DbDefaultPrimaryKey(models.Model):
id = models.DateTimeField(primary_key=True, db_default=Now())

View File

@ -1,3 +1,4 @@
from datetime import datetime
from math import ceil
from operator import attrgetter
@ -23,6 +24,7 @@ from .models import (
BigAutoFieldModel,
Country,
DbDefaultModel,
DbDefaultPrimaryKey,
FieldsWithDbColumns,
NoFields,
NullableFields,
@ -866,3 +868,8 @@ class BulkCreateTests(TestCase):
ctx[0]["sql"].count(created_at_quoted_name),
2 if connection.features.can_return_rows_from_bulk_insert else 1,
)
@skipUnlessDBFeature("can_return_rows_from_bulk_insert")
def test_db_default_primary_key(self):
(obj,) = DbDefaultPrimaryKey.objects.bulk_create([DbDefaultPrimaryKey()])
self.assertIsInstance(obj.id, datetime)