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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With