2010-12-07 43 views
6

J'ai une configuration hiérarchique à deux tables où la table A référence la table B, qui renvoie ensuite à un autre enregistrement dans la table A, et ainsi de suite ... mais seulement jusqu'à une profondeur de récursivité donnée. J'ai cela fonctionne bien en utilisant SQLAlchemy et déclarative. J'utilise également avec succès le chargement impatient avec les propriétés lazy et join_depth sur les relations de table. C'est selon le SQLAlchemy documentation.Comment ajuster dynamiquement la profondeur de récursivité pour un chargement rapide dans SQLAlchemy ORM?

Cependant, cette disposition fixe la profondeur de récursivité à 'join_depth' une fois au chargement du programme ... mais avec les données que j'utilise, je connais la profondeur de récursivité que je devrais utiliser à chaque fois. Comment puis-je modifier la profondeur de récursivité utilisée par requête?

Je l'ai considéré jongler avec le maître propriété join_depth sur l'objet ORM de base, mais cela ne fonctionne pas depuis que j'ai une application scoped_session multi-thread où ce serait dangereux (sans parler du fait que le paramètre est assez difficile à localiser dans SQLAlchemy à l'exécution!).

J'ai également regardé en utilisant joinedload avec la requête mais je ne vois pas comment faire varier la profondeur avec ça. Je suis également au courant de la syntaxe SQL 'WITH RECURSIVE' disponible dans certaines bases de données via CTEs, mais aussi génial que ce soit, je veux éviter cela pour le moment car certains DB ne le supportent toujours pas (et ni SQLAlchemy - du moins pas pour le moment et pas sans beaucoup de personnalisation du dialecte).

Répondre

3

Il n'y a pas de façon officielle de le faire, mais en suivant le code a produit la solution suivante pour moi. J'utilise l'exemple de nœud du docs que vous avez lié.

class Node(Base): 
    __tablename__ = 'node' 
    id = Column(Integer, primary_key=True) 
    parent_id = Column(Integer, ForeignKey('node.id')) 
    data = Column(String(50)) 
    children = relationship("Node", 
        lazy="joined", 
        join_depth=2) 

Au moment de la création, la propriété children reçoit une join_depth de 2. Cette valeur initiale est enregistrée dans Node.children.property.join_depth. Cependant, changer cette valeur ne fera rien. À init, la relation crée une "stratégie" pour la jointure, ce qui copie la valeur join_depth. Pour modifier la profondeur de jointure de la stratégie pour la relation, vous devez définir Node.children.property.strategy.join_depth.

>>> engine.echo = True # print generated queries 
>>> session.query(Node).all() # with default join_depth 
SELECT node.id AS node_id, node.parent_id AS node_parent_id, node.data AS node_data, node_1.id AS node_1_id, node_1.parent_id AS node_1_parent_id, node_1.data AS node_1_data, node_2.id AS node_2_id, node_2.parent_id AS node_2_parent_id, node_2.data AS node_2_data FROM node LEFT OUTER JOIN node AS node_2 ON node.id = node_2.parent_id LEFT OUTER JOIN node AS node_1 ON node_2.id = node_1.parent_id 
>>> Node.children.property.strategy.join_depth = 4 # new join depth 
>>> session.query(Node).all() # with new join depth 
SELECT node.id AS node_id, node.parent_id AS node_parent_id, node.data AS node_data, node_1.id AS node_1_id, node_1.parent_id AS node_1_parent_id, node_1.data AS node_1_data, node_2.id AS node_2_id, node_2.parent_id AS node_2_parent_id, node_2.data AS node_2_data, node_3.id AS node_3_id, node_3.parent_id AS node_3_parent_id, node_3.data AS node_3_data, node_4.id AS node_4_id, node_4.parent_id AS node_4_parent_id, node_4.data AS node_4_data FROM node LEFT OUTER JOIN node AS node_4 ON node.id = node_4.parent_id LEFT OUTER JOIN node AS node_3 ON node_4.id = node_3.parent_id LEFT OUTER JOIN node AS node_2 ON node_3.id = node_2.parent_id LEFT OUTER JOIN node AS node_1 ON node_2.id = node_1.parent_id 

Après avoir réglé Node.children.property.strategy.join_depth, le nombre de jointures dans la requête générée change aussi.