2017-09-10 11 views
-1

からのレコードのリストを取得するために、私は以下のようにDBを持っている:パイソン - SQLAlchemyの:どのように多対多の関係

+-----+  +------+  +-------+  +-------------+ 
|Users|-----|Emails|-----|Assoc_T|------|Other_T  | 
|  |1 m|  |1 m|  |m 1|    | 
|other|  |other |  |other |  |types  | 
|data |  |data |  |data |  |other data | 
+-----+  +------+  +-------+  +-------------+ 

短い説明:私は多くの電子メールを持つことができ、ユーザを、持っているし、この私はSQLの錬金術でUserオブジェクトを持っている場合、メールはOther_T

と多対多の関係を持っています

user = UserModel.query.join(UserModel.Emails).filter_by(Email=id).first() 

は、どのように私は、Auを得るのですか現在のユーザーのOther_Tの固有リスト

以下の方法を使用しますが、実際には動作しても正しく表示されません。あなたが私に教えていない限り(あまりにも多くのネストされたループとDBへのクエリは)*それはここ

class User(object): 
    __DBModel = UserModel() 

    @property 
    def email(self): 
     return self.__DBModel.Emails if self.__DBModel else None 

    def __init__(self, id=None): 
     if Helpers.is_email(str(id)): 
      self.__DBModel = UserModel.query.join(UserModel.Emails).filter_by(Email=id).first() if id else UserModel() 
     elif Helpers.is_number(str(id)): 
      pass 

    # THE QUESTION IS HERE: how to get list of OtherT record based on UserModel defined in __init__? 
    def get_OtherT(self, email=None, other_types=None): 
     # get list of email that the user have 
     emails = [] 
     if not email: 
      emails = self.email.all() 
     else: 
      if Helpers.is_email(str(email)): 
       emails.append(user.email.filter_by(Email=email).first()) 
      else: 
       return False 

     # get list of Other_T_ID in Assoc Table 
     O_T_ID = [] 
     for e in emails: 
      assoc_other_t = e.EmailAssociations 
      for assoc in assoc_other_t: 
       if assoc.Other_T_ID not in O_T_ID: 
        O_T_ID .append(assoc.Other_T_ID) 

     # now, after i have the list of the Other_T ID, get the actual Other_T 
     ret = [] 
     for o in O_T_ID : 
      ret.append(Other_TModel.query.filter_by(Other_T_ID=o, types=other_types).first() 

     return ret 

は私のSQL錬金術モデルである...この方法を実行する必要があります。

UserModel

class UserModel(db.Model): 
    __tablename__ = "Users" 

    UserID = db.Column(db.Integer, primary_key=True, autoincrement=True) 
    FirstName = db.Column(db.String(255), nullable=False) 
    LastName = db.Column(db.String(255), nullable=False) 

    # relationships 
    Emails = db.relationship('EmailModel', backref='user', lazy='dynamic') 

    @orm.reconstructor 
    def init_on_load(self): 
     pass 

    def __init__(self): 
     pass 

EmailModel

class EmailModel(db.Model): 
    __tablename__ = "Emails" 

    Email = db.Column(db.String(255), unique=True, primary_key=True, nullable=False) 
    UserID = db.Column(db.Integer, db.ForeignKey('users.UserID'), nullable=False) 

    # relationships 
    EmailAssociations = db.relationship("Assoc_TModel", back_populates="Emails") 

    @orm.reconstructor 
    def init_on_load(self): 
     pass 

    def __init__(self): 
     pass 

Assoc_TModel

class Assoc_TModel(db.Model): 
    __tablename__ = 'Assoc_T' 

    Other_T_ID = db.Column(
     db.Integer, db.ForeignKey('Other_T.Other_T_ID'), 
     primary_key=True, nullable=False 
    ) 
    Email = db.Column(
     db.String(255), db.ForeignKey('Emails.Email'), 
     primary_key=True, nullable=False 
    ) 
    EmailVerified = db.Column(db.Boolean, nullable=False, server_default='0') 

    # relationships 
    Emails = db.relationship("EmailModel", back_populates="EmailAssociations") 
    Other_Ts = db.relationship("Other_TModel", back_populates="Other_TAssociations") 

    @orm.reconstructor 
    def init_on_load(self): 
     pass 

    def __init__(self): 
     pass 

Other_TModel

class Other_TModel(db.Model): 
    __tablename__ = "Other_T" 

    Other_T_ID = db.Column(db.Integer, nullable=False, unique=True, primary_key=True, autoincrement=True) 
    Other_T_Type = db.Column(db.Enum('one', 'two', 'three'), nullable=False, server_default='one') 
    Other_Data= db.Column(db.String(255), nullable=False) 

    # relationships 
    Other_TAssociations= db.relationship("Assoc_TModel", back_populates="Other_Ts") 
    @orm.reconstructor 
    def init_on_load(self): 
     pass 

    def __init__(self): 
     pass 

ありがとう!

+0

長すぎますか?ほとんど。 https://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-a-very-simple-sql-queryを参照してください。 – Strawberry

+0

あなたが送ったリンクは、私の質問を – AnD

+0

より上にしていません。「私のモデルDBを置く必要があるのなら、時間がかかりすぎます。しかし、見たい場合は教えてください。私はここで共有します。あなたのモデル*は含めないでください。手元の問題を説明するために必要な最小限の完全なモデルが含まれています。 –

答えて

1

この実装では、必要のないデータが過剰に取得されます。

emails = self.email.all()は、すべてのメールエンティティをメモリに読み込みます。

assoc_other_t = e.EmailAssociationsは、それぞれのパフォーマンスに大きな影響を与えるEmailAssociationsごとに追加のSQLクエリをトリガーします。

subqueryを使用すると、中間フェッチを回避できます。

class User(object): 

    def get_OtherT(self, email=None, other_types=None): 
     if email and not Helpers.is_email(email): 
      # I'd recommend an exception here 
      return False 
     # Assoc_TModel subquery 
     # SELECT Other_T_ID FROM Other_T_ID 
     assoc_sq = session.query(Assoc_TModel.Other_T_ID) 

     # Handle email predicate 
     if email: 
      # Specific email 
      assoc_sq = assoc_sq.filter(Assoc_TModel.Email == email) 
     else: 
      # All emails associated with current user 
      email_sq = session.query(EmailModel.Email).\ 
       filter(EmailModel.UserID == self.__DBModel.UserID) 
      assoc_sq = assoc_sq.filter(Assoc_TModel.Email.in_(email_sq)) 

     # Fetch Other_TModel 

     q = session.query(Other_TModel).\ 
      filter(Other_TModel.Other_T_ID.in_(assoc_sq)) 

     if other_types: 
      # unclear `other_types` is a list? 
      q = q.filter(Other_TModel.other_types.in_(other_types)) 
      # or is a scalar value? 
      q = q.filter(Other_TModel.other_types == other_types) 

     return q.all()