Comment faire un upsert avec SqlAlchemy?
J'ai un dossier que je veux exister dans la base de données si elle n'est pas là, et si il y est déjà (clé primaire existe) je veux que les champs à mettre à jour l'état actuel. Ceci est souvent appelé un upsert.
La suite incomplète extrait de code montre ce que sera le travail, mais il semble trop maladroit (surtout si il y avait beaucoup plus de colonnes). Ce qui est le mieux/meilleur moyen?
Base = declarative_base()
class Template(Base):
__tablename__ = 'templates'
id = Column(Integer, primary_key = True)
name = Column(String(80), unique = True, index = True)
template = Column(String(80), unique = True)
description = Column(String(200))
def __init__(self, Name, Template, Desc):
self.name = Name
self.template = Template
self.description = Desc
def UpsertDefaultTemplate():
sess = Session()
desired_default = Template("default", "AABBCC", "This is the default template")
try:
q = sess.query(Template).filter_by(name = desiredDefault.name)
existing_default = q.one()
except sqlalchemy.orm.exc.NoResultFound:
#default does not exist yet, so add it...
sess.add(desired_default)
else:
#default already exists. Make sure the values are what we want...
assert isinstance(existing_default, Template)
existing_default.name = desired_default.name
existing_default.template = desired_default.template
existing_default.description = desired_default.description
sess.flush()
Est-il mieux ou moins détaillé façon de faire cela? Quelque chose comme ce serait génial:
sess.upsert_this(desired_default, unique_key = "name")
bien que le unique_key
kwarg est évidemment inutile (l'ORM devrait être en mesure de facilement comprendre cela) je l'ai ajouté juste parce que SQLAlchemy a tendance à travailler uniquement avec la clé primaire. par exemple: j'ai été regarder si Session.fusion serait applicable, mais cela ne fonctionne que sur la clé primaire, qui dans ce cas est un autoincrementing id qui n'est pas très utile à cette fin.
Un exemple de cas d'utilisation de ce est tout simplement lors du démarrage d'une application serveur qui peut avoir mis à niveau par défaut des données attendues. c'est à dire: pas de simultanéité des préoccupations pour ce upsert.
- Pourquoi ne pouvez-vous pas faire la
name
champ de clé primaire si elle est unique (et de fusion serait de travailler dans ce cas). Pourquoi avez-vous besoin d'une clé primaire distincte? - Je ne veux pas entrer dans un champ id débat, mais... la réponse courte est "clés étrangères". De plus, c'est que même si le nom est en effet la seule clé unique, il y a deux problèmes. 1) lorsqu'un modèle d'enregistrement est référencé par 50 millions d'enregistrements d'une autre table ayant que FK comme un champ de type chaîne est fou. Indexé entier est mieux, donc apparemment inutile colonne id. et 2) s'étendant sur que, si la chaîne a été utilisé comme la FK, il y a maintenant deux emplacements pour mettre à jour le nom de si/quand il change, ce qui est ennuyeux, et en proie à mort les problèmes de relations. L'id ne change jamais.
- vous pouvez essayer une nouvelle (beta) upsert bibliothèque pour python... il est compatible avec les psycopg2, sqlite3, MySQLdb
- voir aussi ce fil: Ne SQLAlchemy ont un équivalent de Django obtenir ou de créer?
Vous devez vous connecter pour publier un commentaire.
SQLAlchemy a un "enregistrer ou mettre à jour" comportement, qui, dans les versions les plus récentes a été construit dans les
session.add
, mais auparavant, il a été séparésession.saveorupdate
appel. Ce n'est pas une "upsert", mais il peut être assez bon pour répondre à vos besoins.Il est bon que vous le posez sur une classe avec plusieurs clés uniques; je crois que c'est précisément la raison il n'y a pas qu'une seule bonne façon de le faire. La clé primaire est également une clé unique. Si il n'y avait pas de contraintes unique, seule la clé primaire, il serait assez simple problème: si de rien avec l'ID existe, ou si l'ID n'est, de créer un nouveau record; d'autre mise à jour de tous les autres champs de l'enregistrement existant avec cette clé primaire.
Cependant, quand il y a d'autres contraintes unique, il est logique de problèmes avec cette approche simple. Si vous voulez "upsert" un objet, et la clé primaire de l'objet correspond à un enregistrement existant, mais d'une autre colonne unique correspond à une différents record, alors que faites-vous? De même, si la clé primaire ne correspond à aucun enregistrement existant, mais d'une autre colonne unique ne correspondre à un enregistrement existant, alors quoi? Il y a peut être une réponse correcte pour votre situation particulière, mais en général, je dirais qu'il n'y a pas qu'une seule bonne réponse.
Qui serait la raison, il n'est pas construit dans le "upsert de l'opération". L'application doit définir ce que cela signifie dans chaque cas particulier.
SQLAlchemy prend en charge
ON CONFLICT
maintenant avec deux méthodeson_conflict_do_update()
eton_conflict_do_nothing()
:La copie de la documentation:
http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html?highlight=conflict#insert-on-conflict-upsert
- Je utiliser une "avant de faire le saut" approche:
L'avantage est que c'est db-neutre et je pense qu'il est clair à lire. L'inconvénient est qu'il y a un potentiel condition de course dans un scénario comme suit:
switch_command
et de ne pas en trouver unswitch_command
switch_command
avec la même clé primaire comme la nôtreswitch_command
De nos jours, SQLAlchemy fournit deux fonctions utiles
on_conflict_do_nothing
eton_conflict_do_update
. Ces fonctions sont utiles, mais vous obliger à swich de l'ORM interface de plus bas niveau d'un SQLAlchemy De Base.Bien que ces deux fonctions permettent de upserting à l'aide de SQLAlchemy de la syntaxe pas que difficile, ces fonctions sont loin d'offrir une gamme complète de out-of-the-box solution à upserting.
Mon utilisation est à upsert un gros morceau de lignes dans une seule requête SQL/session de l'exécution. J'ai l'habitude de rencontrer deux problèmes avec upserting:
Par exemple, une hausse du niveau de la moraine d'oak ridges fonctionnalités, nous avons pris l'habitude de sont manquants. Vous ne pouvez pas utiliser l'ORM objets, mais plutôt de fournir
ForeignKey
s au moment de l'insertion.Je suis en utilisant cette fonction suivante que j'ai écrit pour traiter de ces questions:
Cela fonctionne pour moi avec sqlite3 et postgres. Mais il peut échouer avec le combiné des contraintes de clé primaire et sera très probablement échouer avec d'autres contraintes uniques.
Ci-dessous fonctionne très bien pour moi avec le décalage vers le rouge de la base de données et aussi le travail pour combiné contrainte de clé primaire.
SOURCE : cette
Juste quelques modifications requises pour la création de SQLAlchemy moteur dans la fonction
def start_engine()
Ce qui permet l'accès à la sous-tendent les modèles basés sur des noms de chaîne