web-dev-qa-db-fra.com

Comment obtenir les clés primaires des objets créés à l'aide de Django bulk_create

Existe-t-il un moyen d'obtenir les clés primaires des éléments que vous avez créés à l'aide de la fonction bulk_create dans Django 1.4+?

62
mikec

2016

Depuis Django 1.10 - il est maintenant supporté (sur Postgres uniquement) voici un lien vers la doc .

>>> list_of_objects = Entry.objects.bulk_create([
...     Entry(headline="Django 2.0 Released"),
...     Entry(headline="Django 2.1 Announced"),
...     Entry(headline="Breaking: Django is awesome")
... ])
>>> list_of_objects[0].id
1

Depuis le journal des modifications:

Modifié dans Django 1.10: Ajout de la prise en charge de la définition des clés primaires sur les objets créés à l'aide de bulk_create () lors de l'utilisation de PostgreSQL

51
Or Duan

Selon la documentation, vous ne pouvez pas le faire: https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create

la création en bloc est juste pour cela: créez beaucoup d'objets de manière efficace en sauvant beaucoup de requêtes. Mais cela signifie que la réponse que vous obtenez est un peu incomplète. Si tu fais:

>>> categories = Category.objects.bulk_create([
    Category(titel="Python", user=user),
    Category(titel="Django", user=user),
    Category(titel="HTML5", user=user),
])

>>> [x.pk for x in categories]
[None, None, None]

Cela ne signifie pas que vos catégories n'ont pas pk, juste que la requête ne les a pas récupérées (si la clé est un AutoField). Si vous voulez les pks pour une raison quelconque, vous devrez enregistrer les objets de manière classique.

28
pyriku

Deux approches auxquelles je peux penser:

a) Vous pourriez faire

category_ids = Category.objects.values_list('id', flat=True)
categories = Category.objects.bulk_create([
    Category(title="title1", user=user, created_at=now),
    Category(title="title2", user=user, created_at=now),
    Category(title="title3", user=user, created_at=now),
])
new_categories_ids = Category.objects.exclude(id__in=category_ids).values_list('id', flat=True)

Cela pourrait être un peu cher si l'ensemble de requêtes est extrêmement énorme.

b) Si le modèle a un created_at champ,

now = datetime.datetime.now()
categories = Category.objects.bulk_create([
    Category(title="title1", user=user, created_at=now),
    Category(title="title2", user=user, created_at=now),
    Category(title="title3", user=user, created_at=now),
])

new_cats = Category.objects.filter(created_at >= now).values_list('id', flat=True)

Cela a la limitation d'avoir un champ qui stocke lorsque l'objet a été créé.

23
karthikr

En fait, mon collègue a suggéré la solution suivante qui semble si évidente maintenant. Ajoutez une nouvelle colonne appelée bulk_ref que vous remplissez avec une valeur unique et insérez pour chaque ligne. Ensuite, interrogez simplement la table avec le bulk_ref réglé à l'avance et le tour est joué, vos enregistrements insérés sont récupérés. par exemple.:

cars = [Car(
    model="Ford",
    color="Blue",
    price="5000",
    bulk_ref=5,
),Car(
    model="Honda",
    color="Silver",
    price="6000",
    bulk_ref=5,
)]
Car.objects.bulk_create(cars)
qs = Car.objects.filter(bulk_ref=5)
11
DanH

La solution de contournement la plus simple consiste probablement à affecter manuellement les clés primaires. Cela dépend du cas particulier, mais parfois il suffit de commencer avec max (id) +1 de la table et d'attribuer des nombres incrémentés à chaque objet. Cependant, si plusieurs clients peuvent insérer des enregistrements simultanément, un verrou peut être nécessaire.

0
peper0

Cela ne fonctionne pas en stock Django, mais il y a un patch dans le Django bug tracker qui fait que bulk_create définit les clés primaires pour les objets créés.

0
user3175220

Le documentation Django indique actuellement sous les limitations:

Si la clé primaire du modèle est un champ automatique, il ne récupère pas et ne définit pas l'attribut de clé primaire, comme le fait save().

Mais il y a de bonnes nouvelles. Il y a eu quelques billets parlant de bulk_create de mémoire. Le ticket listé ci-dessus est le plus susceptible d'avoir une solution qui sera bientôt implémentée mais évidemment il n'y a aucune garantie à temps ou si elle arrivera jamais.

Il y a donc deux solutions possibles,

  1. Attendez et voyez si ce patch arrive en production. Vous pouvez aider à cela en testant la solution indiquée et en faisant savoir à la communauté Django vos pensées/problèmes. https://code.djangoproject.com/attachment/ticket/19527 /bulk_create_and_create_schema_Django_v1.5.1.patch

  2. Remplacez/écrivez votre propre solution d'insert en vrac.

0
Matt Seymour