Вопрос

So basically I have multiple render_template returns based on if statements, and they return different variables that my jinja2 template responds to. I believe I could break up these routes into their own functions (likewise I could break up my templates too into more than one template (for example an edit.html template instead of an {% if editform %} in my template)), but I like the idea of having a single view function and template for any given page.

Before I spend more time creating the rest of my view functions, I want to make sure that what I'm doing isn't going to bite me later.

Code below, thanks!

@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)
Это было полезно?

Решение

The answer to this question is probably a little bit down to personal taste, but personally I'd avoid doing things like this. My main reason would just be to keep things simple - you've got three nested if statements inside that function, that are basically doing the exact same job as flasks routing logic would do if you were to split up the views.

Admittedly it's not a massive problem at present, but if you were to add more logic in to any of the paths then it could lead to a surprisingly complex function. This could lead to subtle bugs, or just plain difficulty understanding exactly what's going on from a glance.

If you split things up however, it should be obvious at a glance exactly what each route is doing. Doesn't even involve more code, since you can cut out on all the if statements:

@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)

EDIT: To clarify - I'm not saying it's wrong to have more than one url per flask route. In this case I think it's a bad idea to have a single function doing three fairly different things. There are other use cases for multiple routes per view that I do make use of. For example, in my own code I frequently have views like this:

@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

Where the alternative route accepts a cursor into a database query for pagination. The flow of the code is basically the same, it just has different URLs for the first page and subsequent pages.

Другие советы

There is no hard and fast rule, but I would suggest splitting into multiple functions is more modular going with the Separation of concerns principle.

I would even go one step further and use the MethodView, where there is a class per route with get,post, post etc overridden from base.

When your function is small it looks ok, but when it grows some modularity helps !

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top