J'essaie d'écrire un upsert en vrac dans python en utilisant le module SQLAlchemy (pas en SQL!).
J'obtiens l'erreur suivante sur un ajout SQLAlchemy:
sqlalchemy.exc.IntegrityError: (IntegrityError) duplicate key value violates unique constraint "posts_pkey"
DETAIL: Key (id)=(TEST1234) already exists.
J'ai une table appelée posts
avec une clé primaire sur la colonne id
.
Dans cet exemple, j'ai déjà une ligne dans la base de données avec id=TEST1234
. Lorsque j'essaie de db.session.add()
un nouvel objet de publication avec id
défini sur TEST1234
, J'obtiens l'erreur ci-dessus. J'avais l'impression que si la clé primaire existe déjà, l'enregistrement serait mis à jour.
Comment puis-je effectuer une conversion avec Flask-SQLAlchemy uniquement sur la base de la clé primaire? Existe-t-il une solution simple?
S'il n'y en a pas, je peux toujours vérifier et supprimer tout enregistrement avec un identifiant correspondant, puis insérer le nouvel enregistrement, mais cela semble coûteux pour ma situation, où je n'attends pas beaucoup de mises à jour.
Il y a une opération upsert-esque dans SQLAlchemy:
db.session.merge()
Après avoir trouvé cette commande, j'ai pu effectuer des upserts, mais il convient de mentionner que cette opération est lente pour un "upsert" en vrac.
L'alternative consiste à obtenir une liste des clés primaires que vous souhaitez mettre à jour et à interroger la base de données pour tout identifiant correspondant:
# Imagine that post1, post5, and post1000 are posts objects with ids 1, 5 and 1000 respectively
# The goal is to "upsert" these posts.
# we initialize a dict which maps id to the post object
my_new_posts = {1: post1, 5: post5, 1000: post1000}
for each in posts.query.filter(posts.id.in_(my_new_posts.keys())).all():
# Only merge those posts which already exist in the database
db.session.merge(my_new_posts.pop(each.id))
# Only add those posts which did not exist in the database
db.session.add_all(my_new_posts.values())
# Now we commit our modifications (merges) and inserts (adds) to the database!
db.session.commit()