I have a Flask application with a GET handler that given a recipe id as a URL parameter, it retrieves the recipe from the database and then renders it:
@app.route('/recipe/<int:id>', methods=['GET'])
def get(id):
recipe = get_recipe_from_db(id)
return render_template('recipe.html', recipe=recipe)
This results in a URL like /recipe/5. Rather than showing just the id in the URL, I would like the recipe title to be part of the resulting URL, for example recipe/5/lemon-cake. On the first request only the id is known.
I'm not sure what a neat way to do this is. So far I've come up with the following:
@app.route('/recipe/<int:id>', methods=['GET'])
def get(id):
recipe = get_recipe_from_db(id)
return redirect(url_for('get_with_title', id=id, title=urlify(recipe.title)))
@app.route('/recipe/<int:id>/<title>', methods=['GET'])
def get_with_title(id, title=None):
recipe = get_recipe_from_db(id)
return render_template('recipe.html', recipe=recipe)
This works (i.e. when user visits /recipe/5 it redirects to /recipe/5/lemon-cake) but suffers from the fact that the same recipe is retrieved from the database twice.
Is there a better way to go about this?
Note: the recipe object is large containing multiple fields and I do not want to pass it over the network unecessarily.
CodePudding user response:
Just pass loaded recipe as argument:
@app.route('/recipe/<int:id>', methods=['GET'])
def get(id):
recipe = get_recipe_from_db(id)
return redirect(
url_for(
'get_with_title',
id=id,
title=urlify(recipe.title),
recipe=recipe,
))
@app.route('/recipe/<int:id>/<title>', methods=['GET'])
def get_with_title(id, title=None, recipe=None):
if recipe is None:
recipe = get_recipe_from_db(id)
return render_template('recipe.html', recipe=recipe)
CodePudding user response:
You can look into only changing URL with javascript, as shown here. This way, you don't do any redirects or reloading.
If you don't want to mess with javascript, let's think about backend solutions:
Is loading twice from a database significant performance overhead to be worthy of a complicated solution? If not, your solution is good.
If you really want to avoid loading the whole recipe, you can load only the title in your get method, instead of the whole object.
You can write custom get_recipe_title(id) which does (roughly) SELECT title FROM recipes WHERE id={id}.
If using SQLAlchemy, you may probably use load_only - here
