SQLAlchemy查询多个关系列表

时间:2017-10-10 22:26:45

标签: python sqlalchemy flask-sqlalchemy

我尝试使用Postgresql数据库使用Flask和SQLAlchemy创建后端。在models.py中设置架构时,我有一个Card对象和一个Color对象。有五种颜色,每张卡可以有多种颜色。因此,我尝试使用与Association Object,ColorAssociation的多对多关系。相关的models.py代码如下:

class ColorAssociation(db.Model):
    __tablename__ = 'color_association'
    card_id = db.Column(db.Integer, db.ForeignKey('cards.id'), primary_key=True)
    color_id = db.Column(db.Integer, db.ForeignKey('colors.id'), primary_key=True)
    card = db.relationship("Card", back_populates="colors")
    color = db.relationship("Color", back_populates="cards")

class Color(db.Model):
    __tablename__ = 'colors'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    cards = db.relationship("ColorAssociation", back_populates="color")

class Card(db.Model):
    __tablename__ = 'cards'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    colors = db.relationship("ColorAssociation", back_populates="card")

在添加一些数据之后,架构似乎正常运行,我可以成功查询与颜色相关联的所有卡片,并使用名称' Blue'例如,通过使用以下sqlalchemy查询:

blue = db.session.query(Color).filter(Color.name == 'Blue').all()[0]
for association in blue.cards:
    print(association.card)

我的问题是如何查询多种颜色的卡片?例如,我如何查询所有与颜色相关的卡片,并且名称为' Blue'和'格林'。

1 个答案:

答案 0 :(得分:0)

所以我重新创建了一个测试环境并尝试复制你所遇到的问题,这里常见的缺陷是sqlalchemy在后台提供了一些繁重的工作。由于您将关联放在必须连接到表的另一个模型中,因此在逻辑稍微复杂时会查询。因此,例如,您希望所有蓝色或红色的卡片都是一个衬垫db.session.query(Color).filter(or_(color='blue',color='red'))).all()

但是既然你要求卡片都有这两种颜色,我们需要获得所有独特的卡片并添加其各自的颜色。从那里我们过滤我们想要的颜色(在这种情况下是蓝色和红色)。然后确保每张卡具有我们想要的相同数量的颜色。这是通过使用具有功能来实现的。以下示例。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import random
from sqlalchemy import and_, func, or_

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)


class ColorAssociation(db.Model):
    __tablename__ = 'color_association'
    card_id = db.Column(db.Integer, db.ForeignKey(
        'cards.id'), primary_key=True)
    color_id = db.Column(db.Integer, db.ForeignKey(
        'colors.id'), primary_key=True)
    card = db.relationship("Card", back_populates="colors")
    color = db.relationship("Color", back_populates="cards")


class Color(db.Model):
    __tablename__ = 'colors'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    cards = db.relationship("ColorAssociation", back_populates="color")

    def __repr__(self):
        return "< %d, %s>" % (self.id, self.name)


class Card(db.Model):
    __tablename__ = 'cards'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    colors = db.relationship("ColorAssociation", back_populates="card")


db.create_all()
colors_avail = ['blue', 'red', 'green', 'orange', 'pink']
for x in colors_avail:
    col = Color(name=x)
    db.session.add(col)
db.session.commit()

for x in range(0, 20):
    card = Card(name=str(x) + "card")
    db.session.add(card)
    db.session.commit()
    colors_id = []
    for randnum in range(1, 6):
        rand = random.randint(1, 5)
        if not rand in colors_id:
            colors_id.append(rand)
    for color_id in colors_id:
        association = ColorAssociation(card_id=card.id, color_id=color_id)
        db.session.add(association)
    db.session.commit()
# Get color ids these are unique in this case
colors = db.session.query(Color).filter(
    or_(Color.name == 'blue', Color.name == 'red')).all()

# Create array of these ids
colors_ids = [color.id for color in colors]

cards = db.session.query(Card).distinct(Card.id).outerjoin(ColorAssociation). \
    filter(ColorAssociation.color_id.in_(colors_ids)).group_by(Card.id).having(
        func.count(ColorAssociation.color_id) == len(colors_ids)).all()
"""
This doesn't work
cards = db.session.query(ColorAssociation). \
    filter(ColorAssociation.color_id.in_(colors_ids)).group_by(ColorAssociation.color_id).having(
        func.count(ColorAssociation.color_id) == len(colors_ids)).all()
"""
for card in cards:
    print(card)