2016-04-28 11 views
7

異なるタイプのアイテムのカップルが外部キーを介して親として使用する親モデルがあります。親モデルには多対多の関係もあります。私は多くのモデルをクエリすることに基づいて子モデルを取得しようとしています。多くの関係Flask-SQLAlchemy親モデルと多対多の関係にあるフィルタ

これは、親モデル

class MediaItem(db.Model): 
    __tablename__ = "media_item" 
    id = db.Column(db.Integer, primary_key=True) 
    title = db.Column(db.String, unique=True) 
    tags = db.relationship('Tags', secondary=tags_joiner, backref='media_items') 
    videos = db.relationship('Video', backref='Parent', lazy='dynamic') 
    audios = db.relationship('Audio', backref='Parent', lazy='dynamic') 
    pictures = db.relationship('Picture', backref='Parent', lazy='dynamic') 
    codes = db.relationship('Code', backref='Parent', lazy='dynamic') 

そして、多くのです

class Tags(db.Model): 
    __tablename__ = 'tags' 
    id = db.Column(db.Integer, primary_key=True) 
    tag = db.Column(db.String, unique=True, nullable=False) 


tags_joiner = db.Table('tags_joiner', 
         db.Column('tag_id', db.Integer, db.ForeignKey('tags.id')), 
         db.Column('mediaitem_id', db.Integer, db.ForeignKey('media_item.id')), 
         db.PrimaryKeyConstraint('tag_id', 'mediaitem_id')) 

最後の例では、子モデル

class Video(db.Model): 
    __tablename__ = 'video' 
    id = db.Column(db.Integer, primary_key=True) 
    parent_id = db.Column(db.Integer, db.ForeignKey('media_item.id')) 
    file_name = db.Column(db.String, unique=True) 

foののカップルがありますありますMediaItemモデルで定義された関係によって証明される他のタイプの子モデル。

タグで子モデルのフィルタリングを行う予定です。つまり、特定のタグが指定されている場合は、そのタグに関連付けられているすべての子モデルを返します。 。それはから参加するFROM句が見つかりませんでしたに参加しようとしました(、3つのテーブルを接続する方法を知っているが、得doesntのこと

Video.query.join(media_tags).filter_by(MediaItem.tags.any(Tags.tag.in_(tag))) 

戻り値:「media_item」の間のいずれかの外部キー関係を見つけることができませんし、 'tags')

私のアプローチは何ですか?

答えて

5

バージョン-1:以下のクエリ望ましい結果を返す必要があります。それは2つの入れ子EXISTS句でSQLを生成されますが、間違いなくそれは非常にあるとしてそれは、最も最適ではないかもしれません

tag = 'my_filter_tag' 
q = (
    db.session 
    .query(Video) 
    .filter(Video.Parent.has(MediaItem.tags.any(Tags.tag == tag))) 
) 

を読み取り可能なsqlalchemyクエリ。ここでsqliteのために作成されるクエリは次のとおりです。

SELECT video.id AS video_id, video.parent_id AS video_parent_id, video.file_name AS video_file_name 
FROM video 
WHERE EXISTS (
    SELECT 1 
    FROM media_item 
    WHERE media_item.id = video.parent_id 
     AND (
     EXISTS (
      SELECT 1 
      FROM tags_joiner, tags 
      WHERE media_item.id = tags_joiner.mediaitem_id 
       AND tags.id = tags_joiner.tag_id 
       AND tags.tag = :tag_1 
      ) 
     ) 
    ) 

バージョン-2:ややより最適化されたクエリはmedia_itemに参加することが、その後まだタグにexistsを実行します:

q = (
    db.session 
    .query(Video) 
    .join(MediaItem, Video.Parent) 
    .filter(MediaItem.tags.any(Tags.tag == tag)) 
) 

SELECT video.id AS video_id, video.parent_id AS video_parent_id, video.file_name AS video_file_name 
FROM video 
JOIN media_item 
    ON media_item.id = video.parent_id 
WHERE EXISTS (
    SELECT 1 
    FROM tags_joiner, tags 
    WHERE media_item.id = tags_joiner.mediaitem_id 
     AND tags.id = tags_joiner.tag_id 
     AND tags.tag = :tag_1 
    ) 

あなたができます。以下のようにSQLが生成されます

さらにtags_joinertagsに参加し、結果を得る。しかし、これにより柔軟性が失われます。ORが複数のタグをチェックしている場合、結果はVideoの複数の戻り値を返しますが、EXISTSでクエリを保持すると、それが処理されます。


注意、あなたのコードはmedia_tagsを持っていますが、それが何であるかは明らかではないこと。