2010-04-13 3 views
1

J'essaie de créer une relation ayant une jointure entre. Voici un exemple shortish de ce que je suis en train de faire:Problème avec join pour la relation sqlalchemy orm

#!/usr/bin/env python 
import sqlalchemy as sa 
from sqlalchemy import orm 
from sqlalchemy.ext.declarative import declarative_base 


metadata = sa.MetaData() 
Base = declarative_base(metadata=metadata) 

engine = sa.create_engine('sqlite:///:memory:') 

class Network(Base): 
    __tablename__ = "network" 
    id = sa.Column(sa.Integer, primary_key=True) 
    ip_net_addr_db = sa.Column('ip_net_addr', sa.Integer, index=True) 
    ip_broadcast_addr_db = sa.Column('ip_broadcast_addr', sa.Integer, index=True) 
    # This can be determined from the net address and the net mask, but we store 
    # it in the db so that we can join with the address table. 
    ip_net_mask_len = sa.Column(sa.SmallInteger) 

class Address(Base): 
    __tablename__ = "address" 
    ip_addr_db = sa.Column('ip_addr', sa.Integer, primary_key=True, 
       index=True, unique=True) 

Network.addresses = orm.relation(Address, 
    primaryjoin=Address.ip_addr_db.between(
       Network.ip_net_addr_db, 
       Network.ip_broadcast_addr_db), 
    foreign_keys=[Address.ip_addr_db])  

metadata.create_all(engine) 

Session = orm.sessionmaker(bind=engine) 

Network() 

Si vous exécutez, vous obtiendrez cette erreur:

ArgumentError: Could not determine relation direction for primaryjoin condition 
'address.ip_addr BETWEEN network.ip_net_addr AND network.ip_broadcast_addr', on relation Network.addresses. 
Do the columns in 'foreign_keys' represent only the 'foreign' columns in this join condition ? 

La réponse à cette question est oui, mais je ne peux pas comprendre comment le dire

Répondre

2

SQLAlchemy traverse la condition pour y trouver des paires local-distant pour déterminer les colonnes et la cardinalité de la relation. Cet algorithme est conçu pour fonctionner uniquement pour les opérateurs binaires. La façon simple de résoudre votre problème est de réécrire BETWEEN avec deux opérateurs. Et non, ce ne sont pas les opérateurs « égaux », de sorte que vous ne pouvez pas utiliser cette relation pour ajouter de nouvelles adresses, c'est pourquoi viewonly=True est utilisé:

Network.addresses = orm.relation(Address, 
    viewonly=True, 
    primaryjoin=(
     (Address.ip_addr_db>=Network.ip_net_addr_db) & 
     (Address.ip_addr_db<=Network.ip_broadcast_addr_db) 
    ), 
    foreign_keys=[Address.ip_addr_db] 
) 
+0

Merci. C'est bien. –

Questions connexes