web-dev-qa-db-fra.com

SQLAlchemy - effectuer une mise à jour groupée (s'il existe, mettre à jour, sinon insérer) dans postgresql

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.

29
mgoldwasser

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()
21
mgoldwasser