在Flask视图函数中使用多个路由是坏实践吗?

3 投票
2 回答
5857 浏览
提问于 2025-04-17 22:20

基本上,我有多个 render_template 的返回值,这些返回值是根据条件语句来决定的,它们返回不同的变量,而我的 jinja2 模板会根据这些变量做出反应。我觉得我可以把这些路由分成自己的函数(同样,我也可以把我的模板拆分成多个模板,比如用一个 edit.html 模板,而不是在我的模板里用 {% if editform %}),但我更喜欢为每个页面使用一个单独的视图函数和模板。

在我花更多时间创建其他视图函数之前,我想确保我现在做的不会在以后给我带来麻烦。

下面是代码,谢谢!

@app.route('/admin/users/', defaults={'id': None}, methods = ['GET'])
@app.route('/admin/users/<id>', methods = ['GET'])
@app.route('/admin/users/edit/<id>', methods = ['GET', 'POST'])
@login_required
def users(id):
    if not current_user.role == ROLE_ADMIN:
        flash('You do not have access to view this page.')
        return redirect(url_for('index'))

    if id:
        user = User.query.filter_by(id = id).first()

        if 'edit' in request.path:
            editform = UserForm()

            if editform.validate_on_submit():
                user.username = editform.username.data
                user.email = editform.email.data
                user.role = editform.role.data

                db.session.add(user)
                db.session.commit()

                return redirect('/admin/users/' + str(user.id))

            editform.username.data = user.username
            editform.email.data = user.email
            editform.role.data = user.role

            return render_template("users.html",
                title = "Edit User",
                user = user,
                editform = editform)

        return render_template("users.html",
            title = "User",
            user = user)

    users = User.query.all()

    return render_template("users.html",
        title = 'Users',
        users = users)

2 个回答

1

没有绝对的规则,但我建议把代码分成多个函数,这样更符合关注点分离的原则。

我甚至可以更进一步,使用MethodView,这样每个路由都有一个类,里面可以重写get、post等方法。

当你的函数很小的时候,看起来还不错,但当它变得复杂时,分模块会更有帮助!

10

这个问题的答案可能有点个人偏好,但我个人会避免这样做。我的主要原因就是为了保持简单——在这个函数里有三个嵌套的if语句,它们基本上在做和Flask路由逻辑一样的事情,如果你把视图分开的话。

虽然现在这不是个大问题,但如果你在任何路径中添加更多逻辑,可能会导致这个函数变得意外复杂。这可能会引发一些微妙的错误,或者让人一眼看上去就搞不清楚到底发生了什么。

但是如果你把这些东西分开,那么一眼就能看出每个路由在做什么。这样甚至不需要更多的代码,因为你可以省去所有的if语句:

@app.route('/admin/users/', methods = ['GET'])
def get_all_users():
    users = User.query.all()

    return render_template("users.html",
        title = 'Users',
        users = users)

@app.route('/admin/users/<id>', methods = ['GET'])
def get_single_user(id):
    user = User.query.filter_by(id = id).first()

    return render_template("users.html",
        title = "User",
        user = user)

@app.route('/admin/users/edit/<id>', methods = ['GET', 'POST'])
def edit_user(id):
    editform = UserForm()

    if editform.validate_on_submit():
        user.username = editform.username.data
        user.email = editform.email.data
        user.role = editform.role.data

        db.session.add(user)
        db.session.commit()

        return redirect('/admin/users/' + str(user.id))

    editform.username.data = user.username
    editform.email.data = user.email
    editform.role.data = user.role

    return render_template("users.html",
        title = "Edit User",
        user = user,
        editform = editform)

补充说明:我并不是说每个Flask路由只能有一个URL。在这种情况下,我认为让一个函数做三件相当不同的事情是个坏主意。我确实有其他情况下会用到每个视图多个路由的情况。例如,在我自己的代码中,我经常会有这样的视图:

@migrations_tasks.route('/external_applications', methods=['GET'])
@migrations_tasks.route('/external_applications/<cursor>', methods=['POST'])
def migrate_external_applications(cursor=None, subdomain=None):
    ... do stuff

其中替代路由接受一个数据库查询的游标,用于分页。代码的流程基本上是一样的,只是首页和后续页面有不同的URL。

撰写回答