2016-04-28 10 views
7

Ich habe ein Elternmodell, das ein paar verschiedene Arten von Elementen als ihre Eltern über einen Fremdschlüssel verwenden. Ich habe auch eine Beziehung von vielen zu vielen auf dem Elternmodell. Ich versuche, das Kindmodell basierend auf dem Viel-zu-Vielen-Modell abzufragen.Flask-SQLAlchemy Filter auf viele zu viele Beziehung mit Elternmodell

Dies ist das übergeordnete Modell

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') 

Und die viele zu viele Beziehung

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')) 

Schließlich ein Beispiel fo das Kind Modell

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) 

Es gibt ein paar gibt es andere Arten von Kindmodellen, die durch die im MediaItem-Modell definierten Beziehungen belegt werden.

Ich bin auf der Suche nach Filtern auf das Kind-Modell durch das Tag. Das bedeutet, dass bei einem bestimmten Tag alle untergeordneten Modelle zurückgegeben werden, die diesem Tag zugeordnet sind.

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

Returns, dass es nicht weiß, wie die drei Tabellen zu verbinden, (kann nicht eine FROM-Klausel findet von zu verbinden versuchte Beitritt zu, bekam aber:. Kann keine Fremdschlüsselbeziehungen zwischen ‚media_item‘ gefunden und "Tags".)

Was könnte meine Herangehensweise dazu sein?

Antwort

5

Version-1: Die Abfrage unten sollte das gewünschte Ergebnis zurück:

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

Es ist vielleicht nicht die optimalste sein, wie es produziert SQL mit zwei verschachtelten EXISTS Klauseln ist, aber auf jeden Fall ist es sehr lesbare sqlalchemy Abfrage. Hier ist die Abfrage, die für sqlite hergestellt:

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 
      ) 
     ) 
    ) 

Version-2: Etwas optimierte Abfrage wäre auf der media_item beitreten, aber führen Sie dann noch exists auf dem Tag:

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

, die produzieren die SQL wie folgt:

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 
    ) 

konnte Sie treten Sie auch weiter auf tags_joiner und tags und erzielen Sie das Ergebnis. Dies führt jedoch zu einer gewissen Flexibilität: Wenn Sie mehrere Zeilen durchsuchen und OR nach mehreren Tags suchen möchten, gibt das Ergebnis möglicherweise mehrere Video Zeilen zurück, während die Abfrage mit EXISTS dafür sorgt.


Beachten Sie, dass Ihr Code eine media_tags hat, aber es ist nicht klar, was es ist.

Verwandte Themen