如何将SQL翻译成SQLAlchemy? (将输出模型与DB模型区分开来)

时间:2018-01-12 03:27:42

标签: python sqlalchemy

我有以下SQL请求,它给了我想要的结果,但我无法成功使它在SQLAlchemy中工作。

数据库模型

from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import relationship

db = SQLAlchemy()

class Article(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    image = db.Column(db.String(255))
    brand = db.Column(db.String(255), nullable=False)
    category_id = db.Column(db.Integer, db.ForeignKey("category.id"), nullable=False)
    prices = relationship("ArticlePrice", cascade="all, delete-orphan")

class ArticlePrice(db.Model):
    article_id = db.Column(db.Integer, db.ForeignKey("article.id"), primary_key=True)
    location_id = db.Column(db.Integer, db.ForeignKey("location.id"), primary_key=True)
    price = db.Column(db.Float, nullable=False)
    active = db.Column(db.Boolean, nullable=False)
    location = relationship('Location')

SQL请求(工作)

我想返回所有文章信息,这些信息位于给定位置并在此位置有效。加上price此地点的价格。

SELECT a.*, ap.price
FROM article a
INNER JOIN article_prices ap ON a.id = ap.article_id
WHERE ap.location_id = 1
AND ap.active = true

SQLAlchemy,使用Flask(工作)

@namespace.route('/location/<location_id>')
class ArticleLocatedAPI():
    @namespace.marshal_list_with(article, envelope='articles')
    def get(self, location_id):
        return Article.query.filter(Article.prices.any(active=True, location_id=location_id)).all()

输出模型

正如您所看到的,我希望price找到的文章不是relationship,而是会返回价格数组。

article = namespace.model('Article', {
    'id': fields.Integer,
    'title': fields.String,
    'description': fields.String,
    'image': fields.String,
    'brand': fields.String,
    'price': fields.Float,
    'category_id': fields.Integer
})

问题

上面的代码给了我正确的文章。问题是它正在给我relationship,这给了我一系列的价格。我想返回找到的文章的price。我怎么在SQLAlchemy中写这个?

这是正确的方法吗?

1 个答案:

答案 0 :(得分:3)

您可以使用明确的join()结合contains_eager()选项将Article.prices集合限制为所有价格的子集,如"Using contains_eager() to load a custom-filtered collection result"中所述:

return Article.query.\
    join(Article.prices).\
    filter_by(active=True, location_id=location_id).\
    options(db.contains_eager(Article.prices).load_only("price")).\
    all()

filter_by()Query的主要实体或最后一次加入的目标实体(在本例中为ArticlePrice)中提取其关键字参数。使用load_only()可以限制要从ArticlePrice加载的属性集。除ArticlePrice - you're using an ORM after all的主键外,所生成的SQL与原始SQL完全相同:

SELECT article_price.article_id AS article_price_article_id, article_price.location_id AS article_price_location_id, article_price.price AS article_price_price, article.id AS article_id, article.title AS article_title, article.description AS article_description, article.image AS article_image, article.brand AS article_brand, article.category_id AS article_category_id 
FROM article JOIN article_price ON article.id = article_price.article_id 
WHERE article_price.active = 1 AND article_price.location_id = ?

rename并将价格集合解压缩到标量,请将attribute=关键字参数传递给字段构造函数:

article = namespace.model('Article', {
    'id': fields.Integer,
    'title': fields.String,
    'description': fields.String,
    'image': fields.String,
    'brand': fields.String,
    'price': fields.Float(attribute=lambda a: a.prices[0].price),
    'category_id': fields.Integer
})