SQLAlchemy: expression hybride avec Relation

j'ai deux modèles Flask-SQLAlchemy avec une simple relation de un à plusieurs, comme l'exemple minimal ci-dessous:

class School(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30))
    address = db.Column(db.String(30))

class Teacher(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30))
    id_school = db.Column(db.Integer, db.ForeignKey(School.id))

    school = relationship('School', backref='teachers')

puis j'ajoute une propriété hybride à l'enseignant qui utilise la relation, comme:

@hybrid_property
def school_name(self):
    return self.school.name

Et la propriété fonctionne très bien lorsque je l'utilise comme teacher_instance.school_name. Cependant, je voudrais aussi faire des requêtes comme Teacher.query.filter(Teacher.school_name == 'x'), mais qui me donne une erreur:

`AttributeError: Neither 'InstrumentedAttribute' object nor 
'Comparator' object has an attribute 'school_name'`. 

suite à la documentation de SQLAlchemy, j'ai ajouté une simple expression hybride, comme suit:

@school_name.expression
def school_name(cls):
    return School.name

cependant, quand j'essaye la même requête à nouveau, elle génère une requête SQL sans la clause join, donc j'obtiens toutes les lignes disponibles à L'École, pas seulement celles qui correspondent à la clé étrangère dans Teacher.

à partir de la documentation de SQLAlchemy j'ai réalisé que l'expression attend un contexte où la jointure est déjà présente, donc j'ai essayé de nouveau la requête comme:

Teacher.query.join(School).filter(Teacher.school_name == 'x')

et cela fonctionne réellement, mais cela va à l'encontre du but d'essayer d'obtenir le sucre syntaxique là, en premier lieu, si j'ai besoin de connaître le modèle de l'École pour obtenir de qui. Je m'attends à ce qu'il y ait un moyen d'obtenir cette participation dans l'expression, mais je ne l'ai pas trouvé n'importe où. La documentation a un exemple avec l'expression retournant une sous-requête construit directement avec le select(), mais même ça n'a pas marché pour moi.

des idées?

UPDATE

après la réponse d'Eevee ci-dessous, j'ai utilisé l'association proxy comme l'a suggéré et il fonctionne, mais je suis aussi curieux avec le commentaire qu'il devrait travailler avec l' select() sous-requête et essayé de comprendre ce que j'ai fait de mal. Mon premier essai fut:

@school_name.expression
def school_name(cls):
    return select(School.name).where(cls.id_school == School.id).as_scalar()

Et il s'avère que me donnait une erreur parce que j'ai raté la liste select(). Le code ci-dessous fonctionne très bien:

@school_name.expression
def school_name(cls):
    return select([School.name]).where(cls.id_school == School.id).as_scalar()
22
demandé sur Pedro Werneck 2013-11-05 04:57:37

1 réponses

beaucoup plus simple d'approche pour un cas simple comme c'est un association mandataire:

class Teacher(db.Model):
    school_name = associationproxy('school', 'name')

ceci supporte les questions (au moins avec ==) automatiquement.

je suis curieux de savoir comment l'hybride select() exemple n'a pas fonctionné pour vous, puisque c'est la façon la plus facile de corriger cela au sein d'un hybride. Et pour le plaisir de l'achèvement, vous pourriez également utiliser un transformateur modifier la requête directement plutôt que de la sous-taper.

13
répondu Eevee 2015-11-17 01:52:16