2016-04-28 38 views
7

Ho un modello Parent che un paio di tipi diversi di elementi usano come loro padre tramite una chiave esterna. Ho anche una relazione molte a molte sul modello genitore. Sto cercando di ottenere il modello figlio basato sull'interrogazione del modello da molti a molti.Flask-SQLAlchemy filtro su molti a molti rapporti con il modello principale

Questo è il modello di genitore

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

E la relazione molti a molti

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

Infine v'è un esempio fo il modello bambino

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) 

Ci sono un paio di altri tipi di modelli figlio come evidenziato dalle relazioni definite nel modello MediaItem.

Sto cercando di filtrare il modello figlio dal tag. Vale a dire che, dato un tag specifico, restituisci tutti i modelli figlio associati a quel tag.

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

ritorni che non sa come collegare le tre tabelle, (non riusciva a trovare una clausola FROM di aderire da Provato unirsi a, ma ha ottenuto:. Non riusciamo a trovare nessuna relazioni di chiave esterna tra 'media_item' e 'tags'.)

Quale potrebbe essere il mio approccio a questo?

risposta

5

Versione-1: La domanda sotto dovrebbe restituirla il risultato desiderato:

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

potrebbe non essere il più ottimale, in quanto è produce SQL con due annidati EXISTS clausole, ma sicuramente è molto query leggibile sqlalchemy. Ecco la domanda che viene prodotto per 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 
      ) 
     ) 
    ) 

Version-2: interrogazione Un po 'più ottimizzato sarebbe quella di unirsi al media_item, ma poi ancora eseguire exists sull'etichetta:

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

che produrrà la SQL come di seguito:

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 
    ) 

Si potrebbe anche unirsi ulteriormente su tags_joiner e tags e raggiungere il risultato. Ma questo rimuove un po 'di flessibilità: se si desidera eseguire e controllare OR su più tag, il risultato potrebbe restituire più righe Video, mentre la query con EXISTS si occuperà di ciò.


nota, che il codice ha un media_tags, ma non è chiaro quello che è.