简体   繁体   English

sqlalchemy 子查询和连接没有正确排序关系

[英]sqlalchemy subquery and join not ordering relationship correctly

I have two tables models and images.我有两个表模型和图像。 These tables have a one to many relationship one Model has many Images.这些表具有一对多关系,一个模型有多个图像。 My image sqlalchemy model has an attribute that is a query_expression this is set in the subquery and I want to use it to order both the model results and the images of the models.我的图像 sqlalchemy 模型有一个属性,它是在子查询中设置的 query_expression,我想用它来对模型结果和模型图像进行排序。

using the subquery orders the models but does not order the images of the models.使用子查询对模型进行排序,但不对模型的图像进行排序。 insted the images are ordered by id.插入图像按 id 排序。

subquery does not work quite how I expected it to, because it doesn't seem use the Images from the subquery it appears gets new Images from the database, this does make sense to me but is not what I want it to do.子查询不像我预期的那样工作,因为它似乎没有使用子查询中的图像,它似乎从数据库中获取新图像,这对我来说确实有意义,但不是我想要它做的。 I want it to use the images from the subquery so they have the weight set I think this would fix sorting issue as well.我希望它使用来自子查询的图像,以便它们设置权重,我认为这也可以解决排序问题。 If this is not possible I am also happy to just have the images related to the models sorted correctly matching the subquery如果这是不可能的,我也很高兴将与模型相关的图像正确排序匹配子查询

I have tried to solve this problem in many different ways, This code is the closest i've gotten so far我试图以多种不同的方式解决这个问题,这段代码是我迄今为止最接近的

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
db = SQLAlchemy(app)


class Image(db.Model):
    __tablename__ = 'images'

    id = db.Column(db.Integer, primary_key=True)
    url = db.Column(db.Text)
    model_id = db.Column(db.ForeignKey('models.id'))

    weight = db.query_expression()

    model = db.relationship('Model', primaryjoin='Image.model_id == Model.id', backref="images")

    def __repr__(self):
        return f"<Image {self.id} {self.model_id} {self.weight}>"  # <Image id model_id weight>


class Model(db.Model):
    __tablename__ = 'models'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text)
    url = db.Column(db.Text)


db.create_all()

test_data = [(496, [2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465]),
             (719, [3669, 3670, 3671, 3672, 3673, 3674, 3675, 5122]),
             (720, [3676, 3677, 3678, 3679, 3680, 3681, 3682, 3683, 3684]), (721, [3685, 3686, 3687, 3688, 3689]),
             (957, [4921, 4922, 4923, 4924, 4925, 4926, 4927, 4928])]
for model_id, image_ids in test_data:
    model = Model(id=model_id)
    for image_id in image_ids:
        model.images.append(Image(id=image_id, model_id=model_id))
    db.session.add(model)
db.session.commit()


@app.route('/')
def hello_world():
    image_ids = [5122, 3669, 2457, 3670, 2460, 3677, 2459, 3685, 3676, 2458, 4921, 4923, 3687,
                 4922, 3686, 3671, 4924, 4927, 2461, 3679, 3672, 2465, 3678, 3688, 3682, 3680,
                 4928, 3689, 4925, 3684, 2464, 4926, 3683, 2462, 3675, 3673, 3681, 2463, 3674]
    model_ids = [719, 496, 720, 721, 957]
    image_weights = [0.023905573, 0.82817817, 0.9049727, 0.9182812, 0.9498839, 0.9507337, 0.9895415, 1.0062627,
                     1.006611, 1.0194101, 1.0212375, 1.0346948, 1.053909, 1.095496, 1.0977578, 1.0993682, 1.1059334,
                     1.1750572, 1.1773994, 1.1993531, 1.1995214, 1.2016991, 1.2049828, 1.2159566, 1.2252866, 1.2271863,
                     1.245359, 1.248407, 1.2530898, 1.2567475, 1.2585162, 1.2642251, 1.2729594, 1.2750005, 1.2892585,
                     1.2958231, 1.296642, 1.3050407, 1.310439]

    case_statement = db.case([(uid == Image.id, weight) for uid, weight in zip(image_ids, image_weights)], else_=None) \
        .label("weight")
    images = db.session.query(Image).options(db.with_expression(Image.weight, case_statement)).order_by(
        "weight").filter(db.column("weight").isnot(None))
    images_subquery = images.subquery("images")
    models = db.session.query(Model).join(images_subquery)
    for model in models:
        print(model, model.images)
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

The output when going to the / route is转到 / 路线时的输出是

<Model 719> [<Image 3669 719 None>, <Image 3670 719 None>, <Image 3671 719 None>, <Image 3672 719 None>, <Image 3673 719 None>, <Image 3674 719 None>, <Image 3675 719 None>, <Image 5122 719 None>]
<Model 496> [<Image 2457 496 None>, <Image 2458 496 None>, <Image 2459 496 None>, <Image 2460 496 None>, <Image 2461 496 None>, <Image 2462 496 None>, <Image 2463 496 None>, <Image 2464 496 None>, <Image 2465 496 None>]
<Model 720> [<Image 3676 720 None>, <Image 3677 720 None>, <Image 3678 720 None>, <Image 3679 720 None>, <Image 3680 720 None>, <Image 3681 720 None>, <Image 3682 720 None>, <Image 3683 720 None>, <Image 3684 720 None>]
<Model 721> [<Image 3685 721 None>, <Image 3686 721 None>, <Image 3687 721 None>, <Image 3688 721 None>, <Image 3689 721 None>]
<Model 957> [<Image 4921 957 None>, <Image 4922 957 None>, <Image 4923 957 None>, <Image 4924 957 None>, <Image 4925 957 None>, <Image 4926 957 None>, <Image 4927 957 None>, <Image 4928 957 None>]

as you can see the Models are ordered but the Images are ordered by id and there weight is None I want them to be ordered by their weight and hopefully have it set correctly like this如您所见,模型已排序,但图像按 id 排序,重量为 None 我希望它们按重量排序,并希望像这样正确设置

<Model 719> [<Image 5122 719 0.023905573>, <Image 3669 719 0.82817817>, <Image 3670 719 0.9182812>, <Image 3671 719 1.0993682>, <Image 3672 719 1.1995214>, <Image 3675 719 1.2892585>, <Image 3673 719 1.2958231>, <Image 3674 719 1.310439>]
<Model 496> [<Image 2457 496 0.9049727>, <Image 2460 496 0.9498839>,<Image 2459 496 0.9895415>, <Image 2458 496 1.0194101>, <Image 2461 496 1.1773994>, <Image 2465 496 1.2016991>, <Image 2464 496 1.2585162>, <Image 2462 496 1.2750005>, <Image 2463 496 1.3050407>]
<Model 720> [<Image 3677 720 0.9507337>, <Image 3676 720 1.006611>, <Image 3679 720 1.1993531>, <Image 3678 720 1.2049828>, <Image 3682 720 1.2252866>, <Image 3680 720 1.2271863>, <Image 3684 720 1.2567475>, <Image 3683 720 1.2729594>, <Image 3681 720 1.296642>]
<Model 721> [<Image 3685 721 1.0062627>, <Image 3687 721 1.053909>, <Image 3686 721 1.0977578>, <Image 3688 721 1.2159566>, <Image 3689 721 1.248407>]
<Model 957> [<Image 4921 957 1.0212375>, <Image 4923 957 1.0346948>, <Image 4922 957 1.095496>, <Image 4924 957 1.1059334>, <Image 4927 957 1.1750572>, <Image 4928 957 1.245359>, <Image 4925 957 1.2530898>, <Image 4926 957 1.2642251>]

Any help is greatly appreciated任何帮助是极大的赞赏

After lots of searching through the sqlalchemy docs I found contains_eager .经过大量搜索 sqlalchemy 文档后,我找到了contains_eager It tells SQLAlchemy that the a given relationship should be loaded from the columns in the query.它告诉 SQLAlchemy 应该从查询中的列加载给定的关系。

models = Model.query.join(Image).order_by(case_statement).options(db.contains_eager(Model.images)).filter(Model.id.in_(model_ids))

To make my project work I decided to remove the with_expression and the query_expression and instead just used the case statement in a order_by()为了让我的项目工作,我决定删除with_expressionquery_expression ,而只是在order_by()使用 case 语句

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM