【问题标题】:Optimize ORM queries in Flask using Flask-SQLAlchemy使用 Flask-SQLAlchemy 优化 Flask 中的 ORM 查询
【发布时间】:2020-05-01 20:21:44
【问题描述】:

我使用 Django 和 Flask 编写了相同的项目。整个代码可以在我的 Github 帐户上找到。该网站是一个基于问答的小型测验网站(CTF 格式,问题非常简单)。 以下是链接:

我的问题是关于在 SQLAlchemy 或 Flask-SQLAlchemy 中优化 ORM 查询。

为了更好地理解,我会尽量写出表的架构。

Teams (id, team_name, email, phone, password)
Questions (id, name, body, hint, answer, points, visible)
Submissions (id, team(fk), question(fk), timestamp)

如果您想查看实际代码,请点击此处:
对于 Django - Question & Submission, Team
对于烧瓶 - QuestionTeamSubmission

对于两条路线,/submissions/leaderboard,我必须使用 ORM 编写某些查询。这是页面的样子:

对于 Django,查询看起来很不错(或者至少我是这么认为的:P)

def submissions(request):
    all_submissions = Question.objects \
        .values('id', 'name') \
        .order_by('id') \
        .annotate(submissions=Count('submission'))

    print(all_submissions.query)

    return render(request, 'questions/submissions.html', {
        'submissions': all_submissions
    })

def leaderboard(request):
    team_scores = Team.objects \
        .values('team_name') \
        .order_by('team_name') \
        .annotate(score=Coalesce(Sum('submission__question__points'), 0)) \
        .order_by('-score')

    print(team_scores.query)

    return render(request, 'questions/leaderboard.html', {
        'team_scores': team_scores,
    })

原始 SQL 查询如下所示:

SELECT "teams_team"."team_name", COALESCE(SUM("questions_question"."points"), 0) AS "score" FROM "teams_team" LEFT OUTER JOIN "questions_submission" ON ("teams_team"."id" = "questions_submission"."team_id") LEFT OUTER JOIN "questions_question" ON ("questions_submission"."question_id" = "questions_question"."id") GROUP BY "teams_team"."team_name" ORDER BY "score" DESC

SELECT "questions_question"."id", "questions_question"."name", COUNT("questions_submission"."id") AS "submissions" FROM "questions_question" LEFT OUTER JOIN "questions_submission" ON ("questions_question"."id" = "questions_submission"."question_id") GROUP BY "questions_question"."id", "questions_question"."name" ORDER BY "questions_question"."id" ASC

对我的问题的介绍非常冗长。

我的问题从这里开始,我似乎无法使用 SQLAlchemy ORM 编写这个或类似的查询,并且 PyCharm 没有提供正确的代码完成/建议。

对于 Flask,我的函数如下所示:

def get_team_score(team):
    team_submissions = Submission.query.filter_by(team_id=team.id)
    score = sum(
        submission.question.points
        for submission in team_submissions
    )

    return score

@question_blueprint.route('/submissions')
def submissions():
    all_submissions = [
        {
            'id': q.id,
            'name': q.name,
            'submissions': Submission.query.filter_by(question_id=q.id).count()
        }
        for q in Question.get()  # fetch all Question rows
    ]
    return render_template('submissions.html', **{
        'submissions': all_submissions
    })

@question_blueprint.route('/leaderboard')
def leaderboard():
    team_scores = [
        {
            'team_name': team.team_name,
            'score': get_team_score(team)
        }
        for team in Team.query.filter_by()
    ]
    return render_template('leaderboard.html', **{
        'team_scores': team_scores
    })

查询没有优化,我想知道是否可以编写像 django-orm 这样的优雅查询,而无需编写原始 SQL 语句。如果可能的话,我想对这个问题中提到的这两条路线进行一些很好的优化查询。

呼。

【问题讨论】:

    标签: python django flask flask-sqlalchemy django-orm


    【解决方案1】:

    您可以像这样更改上面的代码。

    @question_blueprint.route('/leaderboard')
    def leaderboard():
        team_scores =\
            Team.query.join(
                Submission,
            ).join(
                Question,
            ).with_entities(
                Team.team_name,
                func.sum(Question.points).label("score")
            ).all()
    
        return render_template('leaderboard.html', **{
            'team_scores': team_scores
        })
    

    在下面的这个 sn-p 中,您加入团队 -> 提交 -> 问题,然后因为您只需要基于每次提交的问题的团队名称和总分,所以您只需要 with_entities

    @question_blueprint.route('/submissions')
    def submissions():
        all_submissions = Question.query.join(
            Submission
        ).with_entities(
            Question.id,
            Question.name,
            func.count().label("submissions")
        ).all()
    
        return render_template('submissions.html', **{
            'submissions': all_submissions
        })
    
    

    要查询提交的问题,你可以像我上面添加的那样做类似的查询,但不是计算你只想计算行的总和,所以你只需将其更改为 func.sum

    【讨论】:

    • 对于排行榜逻辑,我在加入后使用了 group_by,但它没有显示得分为 0 的团队。
    【解决方案2】:

    经过Kelvin 的回答后,我有点明白我需要如何在 SQLAlchemy 中运行查询。我不断修改 Kelvin 的答案以满足我的需要,这些都是完全有效的查询。

    @question_blueprint.route('/submissions')
    def submissions():
        all_submissions = Question.query \
            .outerjoin(Submission) \
            .group_by(Question.id) \
            .order_by(Question.id) \
            .with_entities(
                Question.id,
                Question.name,
                func.count(Submission.id).label('submissions')) \
            .all()
    
        return render_template('submissions.html', **{
            'submissions': all_submissions
        })
    
    
    @question_blueprint.route('/leaderboard')
    def leaderboard():
        team_scores = Team.query \
            .outerjoin(Submission) \
            .outerjoin(Question) \
            .group_by(Team.team_name) \
            .with_entities(
                Team.team_name,
                func.coalesce(func.sum(Question.points), 0).label('score')) \
            .with_labels().all()
    
        return render_template('leaderboard.html', **{
            'team_scores': team_scores
        })
    

    【讨论】:

      猜你喜欢
      • 2019-05-18
      • 2013-07-24
      • 1970-01-01
      • 2014-10-09
      • 2017-06-08
      • 2018-05-09
      • 2019-04-23
      • 2019-12-13
      • 2017-08-28
      相关资源
      最近更新 更多