2017-09-03 10 views
0

私はSqlalchemyで何かできるかどうか、あるいは間違った方法を考えているかどうかを理解しようとしています。Pythonのsqlalchemyの動的関係

class Customer(db.Model): 
    __tablename__ = 'customer' 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    addresses = relationship('Address') 

class Address(db.Model): 
    __tablename__ = 'address' 
    if = Column(Integer, primary_key=True) 
    address = Column(String) 
    home = Column(Boolean) 
    customer_id = Column(Integer, ForeignKey('customer.id')) 

以降、私は顧客とちょうど彼らのホームアドレスを取得するクエリを実行します:例として、私は2つ(これらは一例です)のクラスを持っていると言います。

db.session.query(Customer).join(Address, Address.home == True) 

以上の更なる結果は、唯一のホームアドレスを取得しますので、参加を制限/絞り込むでしょう:それはこのようなものであることを行うことは可能ですか?事前に

おかげで、 ダグ

答えて

0

うん、あなたはおそらくのようなコードたいと思うけれどもそれは、完全に可能です:

# if you know the customer's database id... 
# get the first address in the database for the given id that says it's for home 
home_address = db.session.query(Address).filter_by(customer_id=customer_id_here, home=True).first() 

代わりに家のためのブール値を持つのは、あなたが「タイプ」を試してみてください代わりに列を使用してください。これは、 "このアドレスは自宅用かどうか"のバイナリの選択ではなく、仕事のような場所のアドレスを簡単に選択できるようにします。

更新:back_populatesキーワード引数をリレーションシップ呼び出しで使用することも考えられます。したがって、アドレスインスタンス(a)がある場合は、顧客がa.customer(インスタンスこの住所が関連付けられている顧客クラスの)。疑いで、クエリ構築物はあなたが望むものである場合とき

+0

感謝を使用することができます。代わりに、custom join conditionをお客様に一対一の関係に新しいものを追加する必要があります。私は私の質問についてより明確にすべきだった、2つのクラスは私が問題を説明するために使用している単なる例です。この例のクラスは、1対2の関係をモデル化し、ForeignKeyは結合を指定します。顧客を獲得する簡単なクエリを実行する場合、住所関係コレクションにはその顧客のすべての住所も含まれます。しかし、私が望むのは、顧客を取得するために照会することができるように結合基準をオーバーライドまたは追加することですが、リレーションシップ・コレクションには1つのエレメント、つまりホーム・アドレスしかありません。 –

1

、それを印刷してみてください:これはあなたが望むものではないことは明らかである

In [29]: db.session.query(Customer).join(Address, Address.home == True) 
Out[29]: <sqlalchemy.orm.query.Query at 0x7f14fa651e80> 

In [30]: print(_) 
SELECT customer.id AS customer_id, customer.name AS customer_name 
FROM customer JOIN address ON address.home = true 

。すべての顧客は、自宅の住所であるすべての住所に参加します。エンティティの処理方法によって、これは最初は明白ではないかもしれません。顧客あたりの重複行は無視され、基礎となる照会が間違っていても、別個のCustomerエンティティーの結果が得られます。結果を形成するときに、結合されたアドレスを効果的に無視するだけです。

db.session.query(Customer, Address).\ 
    join(Address).\ 
    filter(Address.home) 

あなたはまた、この

db.session.query(Customer).\ 
    join(Address, (Customer.id == Address.customer_id) & Address.home).\ 
    options(contains_eager(Customer.addresses)) 

ような何かを行うことができますが、私は非常にそれに対してお勧めします:

最も簡単な解決策は、単に必要な基準を顧客とアドレスのタプルを照会することです。関係のコレクションに含まれていることについて、あなたは自分自身に嘘をついています。

class Customer(db.Model): 
    ... 
    home_address = relationship(
     'Address', uselist=False, 
     primaryjoin='and_(Customer.id == Address.customer_id, Address.home)') 

をし、その後、あなたは参加し熱心な負荷に返信用

db.session.query(Customer).options(joinedload(Customer.home_address)) 
+0

@ writes-onここで編集として提案したあなたのコメントに、「これは関数呼び出しであり、home_onlyはその関数のパラメータであるため、すべての顧客アドレスを得ることができます。あなたはこれに躊躇していることを説明します(あなたの例に近いからです)? - 私は、今のようには見えないかもしれないが、あなたのところに戻って来るかもしれないので、その関係のためのそのような二重の役割を実装することに消極的であろう。将来、その関数呼び出しのオブジェクトは、あなたや他の誰かがコレクションにすべてのアドレスを保持すると期待するどこかで終わる可能性があります。 –

+0

...そして、このような "ホーム"関係を別のもの1つの関係では、アドレスの集合がすべてのアドレスまたは家だけを保持するような特殊なケースを作成することによって、システムに複雑さを加えることはほとんどありません。しかし、いつものように、ymmv、そして解決策があなたのために働くのであれば、誰が議論するのですか。 –

+0

もう一度お返事ありがとうございます、これまでのところ私のために働いています。単純なpriaryjoinでモデルの定義を私にすべてのアドレスを持つ顧客を取得します。しかし、実行時にクエリを修正して家庭用のフィルタを追加すると、私はただ1つの(自宅)アドレスだけを持つ顧客を得ます。 :) –

関連する問題