178 votes

Insertion en bloc avec SQLAlchemy ORM

Est-il possible de SQLAlchemy pour faire une insertion en bloc plutôt que de l'insertion de chaque objet. c'est à dire,

faire:

INSERT INTO `foo` (`bar`) VALUES (1), (2), (3)

plutôt que:

INSERT INTO `foo` (`bar`) VALUES (1)
INSERT INTO `foo` (`bar`) VALUES (2)
INSERT INTO `foo` (`bar`) VALUES (3)

J'ai juste converti un peu de code pour utiliser sqlalchemy plutôt que brut sql et bien il est maintenant beaucoup plus agréable de travailler avec elle semble être plus lent maintenant (jusqu'à un facteur 10), je me demande si c'est la raison.

Peut-être que je pourrait améliorer la situation à l'aide de sessions de manière plus efficace. Pour le moment j'ai autoCommit=False et faire un session.commit() après j'ai ajouté quelques trucs. Bien que cela semble provoquer les données à jour si la DB est changé ailleurs, que même si je fais une nouvelle requête, je reçois toujours la vieille résultats?

Merci pour votre aide!

36voto

dhaffey Points 758

Autant que je sache, il n'y a aucun moyen d'obtenir l'ORM à la question des insertions. Je crois que la raison sous-jacente est que SQLAlchemy besoin de garder une trace de chaque objet de l'identité (par exemple, de nouvelles clés primaires), et des insertions les en empêcher. Par exemple, en supposant que votre foo tableau contient un id colonne et correspond à un Foo classe:

x = Foo(bar=1)
print x.id
# None
session.add(x)
session.flush()
# BEGIN
# INSERT INTO foo (bar) VALUES(1)
# COMMIT
print x.id
# 1

Depuis SQLAlchemy ramassé la valeur de x.id sans émission d'une autre requête, nous pouvons en déduire qu'il a obtenu la valeur directement à partir de l' INSERT déclaration. Si vous n'avez pas besoin de l'accès ultérieur à la création des objets via le même cas, vous pouvez ignorer la couche ORM pour votre encart:

Foo.__table__.insert().execute([{'bar': 1}, {'bar': 2}, {'bar': 3}])
# INSERT INTO foo (bar) VALUES ((1,), (2,), (3,))

SQLAlchemy ne peut pas correspondre à ces nouvelles lignes avec les objets existants, de sorte que vous aurez à les interroger à nouveau pour toute la suite des opérations.

Autant que des données obsolètes, il est utile de rappeler que la session n'a pas de moyen de savoir si la base de données est modifié en dehors de la session. Afin d'accéder à l'extérieur données modifiées par les instances existantes, le cas, il doit être marqué comme expiré. Ce qui se passe par défaut sur session.commit(), mais peut être fait manuellement en appelant session.expire_all() ou session.expire(instance). Un exemple SQL (omis):

x = Foo(bar=1)
session.add(x)
session.commit()
print x.bar
# 1
foo.update().execute(bar=42)
print x.bar
# 1
session.expire(x)
print x.bar
# 42

session.commit() expire x, de sorte que la première instruction print implicitement ouvre une nouvelle transaction et re-requêtes xs'attributs. Si vous commentez la première instruction d'impression, vous remarquerez que le second reprennent la valeur correcte, parce que la nouvelle requête n'est pas émis jusqu'à ce qu'après la mise à jour.

Cela fait sens du point de vue de l'isolation transactionnelle - vous ne devez ramasser externe modifications entre les transactions. Si cela vous cause problème, je te suggère de clarifier ou de re-penser de votre application, les limites de transaction au lieu de rejoindre immédiatement pour session.expire_all().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X