具有多个联接的SQLAlchemy查询

时间:2017-07-06 12:26:46

标签: python postgresql flask sqlalchemy flask-sqlalchemy

我正在使用SQLAlchemy和Postgres创建一个烧瓶应用程序。我很绿,所以我会得到任何反馈意见。但是,我的直接问题是在以下模型上构建查询。

from app import db
from sqlalchemy import or_, and_


# Items Table
class Item(db.Model):

    __tablename__ = "items"

    id = db.Column(db.Integer, primary_key=True)
    itemName = db.Column(db.String, unique=True, nullable=False)
    measurement = db.Column(db.String, nullable=False)
    defaultPrice = db.Column(db.Float, nullable=False)
    minimumOrder = db.Column(db.Float, nullable=False)
    maximumOrder = db.Column(db.Float, nullable=False)
    orders = db.relationship('Order', back_populates='item')
    prices = db.relationship('Price', back_populates='item')

    def __init__(self, itemName, measurement, defaultPrice,
                 minimumOrder, maximumOrder):
        self.itemName = itemName
        self.measurement = measurement
        self.defaultPrice = defaultPrice
        self.minimumOrder = minimumOrder
        self.maximumOrder = maximumOrder

    def __repr__(self):
        return '<Item {0}>'.format(self.id)


# Users Table
class User(db.Model):

    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    fullName = db.Column(db.String, unique=True, nullable=False)
    userName = db.Column(db.String, unique=True, nullable=False)
    password = db.Column(db.String, nullable=False)
    role = db.Column(db.String, nullable=False)
    orders = db.relationship('Order', back_populates='user')
    prices = db.relationship('Price', back_populates='user')

    def __init__(self, fullName, userName, password, role):
        self.fullName = fullName
        self.userName = userName
        self.password = password
        self.role = role

    def __repr__(self):
        return '<User {0}>'.format(self.userName)


# Availability / Price Table
class Price(db.Model):

    __tablename__ = 'prices'

    id = db.Column(db.Integer, primary_key=True)
    userId = db.Column(db.Integer, db.ForeignKey('users.id'))
    user = db.relationship('User', back_populates='prices')
    itemId = db.Column(db.Integer, db.ForeignKey('items.id'))
    item = db.relationship('Item', back_populates='prices')
    available = db.Column(db.Boolean)
    priceMeasurement = db.Column(db.String)
    price = db.Column(db.Float)

    def __init__(self, userId, itemId, priceMeasurement, price):
        self.userId = userId
        self.itemId = itemId
        self.priceMeasurement = priceMeasurement
        self.price = price

    def __repr__(self):
        return '<Price {0}>'.format(self.price)


# Orders Table
class Order(db.Model):

    __tablename__ = 'orders'

    id = db.Column(db.Integer, primary_key=True)
    userId = db.Column(db.Integer, db.ForeignKey('users.id'))
    user = db.relationship('User', back_populates='orders')
    itemId = db.Column(db.Integer, db.ForeignKey('items.id'))
    item = db.relationship('Item', back_populates='orders')
    orderQuantity = db.Column(db.Float)
    orderMeasurement = db.Column(db.String)
    orderPrice = db.Column(db.Float)
    orderDelivery = db.Column(db.Date)
    orderPlaced = db.Column(db.Date)

    def __init__(self, userId, itemId, orderQuantity,
                 orderMeasurement, orderPrice, orderDelivery, orderPlaced):
        self.userId = userId
        self.itemId = itemId
        self.orderQuantity = orderQuantity
        self.orderMeasurement = orderMeasurement
        self.orderPrice = orderPrice
        self.orderDelivery = orderDelivery
        self.orderPlaced = orderPlaced

    def __repr__(self):
        return '<Order {0}>'.format(self.orderDelivery)

我希望从查询中返回一个类似于以下查询返回的表:

SELECT * FROM items
JOIN prices ON prices.itemId=items.id
WHERE prices.userId = 1 AND prices.available = True
LEFT JOIN (
SELECT * FROM orders WHERE orderDelivery = '2017-07-05') as orders
ON orders.itemId=items.id

在SQLAlchemy查询中。我将把userId和orderDelivery变量传递给路由和会话中的查询 - @app.route('/user/order/<order_date>') | session['userID']:在登录时建立。

由于

1 个答案:

答案 0 :(得分:4)

如果我理解正确,您需要查询(Item, Price, Order)权限的元组,其中Order来自子查询。这在Selecting Entities from Subqueries下的对象关系教程中进行了解释。

In [5]: from datetime import date

In [6]: orders_sq = db.session.query(Order).\
   ...:     filter(Order.orderDelivery == date(2017, 7, 5)).\
   ...:     subquery()

In [7]: orders_alias = db.aliased(Order, orders_sq)

In [8]: query = db.session.query(Item, Price, orders_alias).\
   ...:     join(Price).\
   ...:     outerjoin(orders_alias, Item.orders).\
   ...:     filter(Price.userId == 1,
   ...:            Price.available)

和针对SQLite编译时产生的SQL:

In [9]: print(query)
SELECT items.id AS items_id, items."itemName" AS "items_itemName", items.measurement AS items_measurement, items."defaultPrice" AS "items_defaultPrice", items."minimumOrder" AS "items_minimumOrder", items."maximumOrder" AS "items_maximumOrder", prices.id AS prices_id, prices."userId" AS "prices_userId", prices."itemId" AS "prices_itemId", prices.available AS prices_available, prices."priceMeasurement" AS "prices_priceMeasurement", prices.price AS prices_price, anon_1.id AS anon_1_id, anon_1."userId" AS "anon_1_userId", anon_1."itemId" AS "anon_1_itemId", anon_1."orderQuantity" AS "anon_1_orderQuantity", anon_1."orderMeasurement" AS "anon_1_orderMeasurement", anon_1."orderPrice" AS "anon_1_orderPrice", anon_1."orderDelivery" AS "anon_1_orderDelivery", anon_1."orderPlaced" AS "anon_1_orderPlaced" 
FROM items JOIN prices ON items.id = prices."itemId" LEFT OUTER JOIN (SELECT orders.id AS id, orders."userId" AS "userId", orders."itemId" AS "itemId", orders."orderQuantity" AS "orderQuantity", orders."orderMeasurement" AS "orderMeasurement", orders."orderPrice" AS "orderPrice", orders."orderDelivery" AS "orderDelivery", orders."orderPlaced" AS "orderPlaced" 
FROM orders 
WHERE orders."orderDelivery" = ?) AS anon_1 ON items.id = anon_1."itemId" 
WHERE prices."userId" = ? AND prices.available = 1

另外,您也可以通过一些修复和更改将您的语句简单地传递给Query.from_statement

In [45]: query2 = db.session.query(Item, Price, Order).\
    ...:     from_statement(db.text("""
    ...: SELECT * FROM items
    ...: JOIN prices ON prices.itemId=items.id
    ...: LEFT JOIN (
    ...: SELECT * FROM orders WHERE orderDelivery = :orderDelivery) as orders
    ...: ON orders.itemId=items.id
    ...: WHERE prices.userId = :userId AND prices.available
    ...: """)).\
    ...:     params(userId=1, orderDelivery='2017-07-05')

但我建议使用前一种方法,因为它更加数据库不可知。