2011-09-14 2 views
1

Quelqu'un peut-il m'aider à obtenir la grande image lorsqu'il s'agit de configurer des modèles SQLAlchemy, donc l'intégrité référentielle est assurée à tous les niveaux? Partant de l'idée que l'intégrité référentielle devrait être exprimée et appliquée par la DB, j'ai créé un schéma (actuellement dans Postgresql) avec toutes les contraintes dont je pense avoir besoin, me donnant ainsi une base de données qui fera confiance à l'intégrité référentielle .Intégrité référentielle - comment configurer SQLAlchemy?

Je commence alors à construire une application au-dessus de cette base de données, en utilisant SQLAlchemy (0.7) en mode déclaratif.

Après avoir cherché et lu un peu, je l'ai appris que je peux configurer:

  • OnUpdate/règles onDelete sur ma colonne() définitions.
  • options en cascade sur mes définitions relation(),
    et que celles-ci semblent fonctionner au niveau de la session dans SQLAlchemy.
  • options passive_deletes et passive_updates pour mes définitions relationship().

Et que toutes ces options ont des valeurs par défaut.

Mais je suis confus quant à ce que j'ai réellement besoin de faire avec mes modèles SQLAlchemy, pour m'assurer que SQLAlchemy ne soit pas désynchronisé avec la base de données et ses contraintes pendant une session.

Que dois-je faire si je configure 'onupdate' etc. sur mes définitions Columns() dans SQLAlchemy?

Et pour les règles cascade et passive_delete/passive_update je peux configurer sur une relation(). De quoi ai-je besoin ici, et pourquoi?

Ou de reformuler ma question: Dans quelle mesure SQLAlchemy sera-t-il conscient des contraintes configurées dans le schéma DB, et dans quelle mesure (et comment) dois-je les répéter dans mes modèles?

Et est le autre chose que je devrais être au courant de? :)

Répondre

4

SQLAlchemy n'a pas fondamentalement besoin de savoir quoi que ce soit sur les contraintes de votre base de données. Si votre base de données a les contraintes que vous voulez configurer, vous avez essentiellement terminé - votre application n'est pas autorisée par la base de données à faire ce qu'elle n'est pas supposée faire.

Un thème clé de SQLAlchemy est qu'il ne fait vraiment que ce que vous lui dites. Donc, si vous essayez de persister un objet, SubWidget(), qui dans la base de données doit avoir une référence à un parent Widget(), au moment où SQLAlchemy videra les données (c.-à-d. Émet des instructions INSERT), l'opération échouera avec une violation de contrainte, émise par la base de données, et la transaction est annulée. Donc, en supposant un FK sur "subwidget" référençant "widget", votre application doit s'assurer que les données sont dans la bonne structure. Il y a deux façons de faire ça; La première consiste à gérer manuellement les colonnes contenant des références de clé étrangère et à s'assurer qu'elles ont la valeur appropriée au niveau de INSERT ou UPDATE. L'autre est que vous utiliseriez relationship() pour gérer l'attribut de clé étrangère, et que vous feriez plutôt en sorte que la création d'un objet SubWidget() soit accompagnée de l'opération de l'associer à un objet Widget() parent que vous avez créé et/ou acquis séparément.En ce qui concerne les cascades, c'est une bonne idée, mais pas obligatoire, d'avoir ON DELETE CASCADE sur les clés étrangères où cela s'applique. Du côté de SQLAlchemy, lorsque vous utilisez relationship(), vous voulez généralement donner à l'ORM un indice que la base de données cascade les suppressions via l'indicateur passive_deletes (http://www.sqlalchemy.org/docs/orm/collections.html?highlight=passive_deletes#using-passive-deletes), mais il s'agit généralement d'une amélioration des performances; SQLAlchemy vérifie sinon que tous les objets représentés sur le côté dépendant du relationship() sont chargés en mémoire et gérés de manière appropriée, ce qui signifie soit de définir l'attribut de clé étrangère sur NULL (par défaut), soit de marquer l'objet dépendant pour suppression (qui se produit en définissant "cascade" sur "all, delete-orphelin", voir http://www.sqlalchemy.org/docs/orm/session.html#cascades).

SUR MISE À JOUR La cascade est moins courante car les clés primaires naturelles ne sont plus une pratique courante ces jours-ci, car elles ne fonctionnent pas aussi bien que les clés primaires entières et peuvent également être fastidieuses d'autres façons. Cependant, SQLAlchemy les prend également en charge et prendra généralement soin d'eux-mêmes, car SQLA suppose par défaut que les cascades de mise à jour sont en place lorsqu'une mutation PK a lieu, voir http://www.sqlalchemy.org/docs/orm/relationships.html#mutable-primary-keys-update-cascades pour une description détaillée de cela. Peut-être que tout cela est plus facile à faire avec un peu d'expérimentation, l'idée de base est que SQLAlchemy n'émet que le SQL que vous lui dites, même si beaucoup de ses comportements SQL sont automatisés une fois configurés à l'avance. relationship() doit être configuré avec des détails sur la manière dont vous souhaitez qu'il se comporte lorsque les données sont conservées, modifiées ou supprimées par rapport aux contraintes présentes dans la base de données.

+0

Salut zzzeek, ​​ Merci d'essayer de lever une partie du brouillard dans ma tête. Et pour SQLAlchemy lui-même! :) J'ai ajouté une "réponse" à ma propre question, où j'essaie de résumer ce que je pense avoir appris. Espérons que je suis sur la bonne voie ... – herira

1

Ainsi, la construction sur zzzeeks réponse, et ma propre étude/bricoler après ma question initiale ...

Pour faire SQLAlchemy activement éviter qu'une vue en session de l'état DB peut détourner de ce que le DB vous autorisera lorsque vous videz/commit, vous devez refléter toutes les contraintes trouvées dans le schéma DB dans vos modèles SQLAlchemy.

Et cela se fait via les définitions de la colonne sous forme de:

ForeignKey(..., onupdate='', ondelete='') 
primary_key=True 
unique=True 

et ainsi de suite, avec l'inclusion possible de __table_args__, comme:

__table_args__ = (
     ForeignKeyConstraint(['id'], ['remote_table.id']), 
     UniqueConstraint('foo'), 
     ) 

Pour les cas où les durées de contrainte plusieurs colonnes.

Attendu que:

relationship() 

et ses arguments connexes, tels que:

cascade 
cascade_backrefs 
foreign_keys 
passive_deletes 
passive_updates 

et ainsi de suite, sont un (important) fonctionnalité pratique qui vous permet de travailler avec vos modèles avec aussi peu d'effort que possible, mais ultime ne sont pas destinés à être ce que empêche violation de l'intégrité référentielle.

La fonction relation() ne peut pas exprimer toutes les contraintes typiques d'une base de données, contrairement à la fonction Column() (et __table_args__). D'autre part, en configurant la relation() avec certains des arguments listés ci-dessus (ou en passant par les valeurs par défaut si cela a le plus de sens), SQLAlchemy va effectuer des tâches automatiquement, ce qui peut être dit référentiel. en relation. Et devrait normalement normalement être exprimé par la logique dans le code environnant.

La configuration optimale de relation() permettra également, dans certains cas, d'éviter l'émission d'instructions SQL inutiles par SQLAlchemy.

Hope this somme est un peu précis ...

+0

écrémage brièvement me semble bien – zzzeek

Questions connexes