[Flask] Populate form with multiple item from database
Philippe Le Toquin
philippe at ppmt.org
Tue Feb 9 17:00:49 EST 2016
Hi,
Very new to this mailing and first post so not sure if I am in the right
place for my question. If it is not appropriate or if pasting so much
code is wrong, I apologise in advance!
And would appreciate if you could redirect me to a better place.
I am trying to write an application for entering recipe.
I am not expert in Python but by reading and with lots of trial and
errors, I normally
get the result I need.
But lately I have been stuck on a problem which I believe is beyond my
understanding of programming. I have a form which I use to add
ingredient to a recipe
Since it can have more than one ingredient I use some javascript code to
clone the form for each additional ingredient. Once submitted it is then
saved in a table.
That part is working even though I am sure the code would scare most of you.
Where I am stuck is that when I want to edit the ingredient list. I
can't find a way to populate the form with the list of ingredient that
are already
in the table for that recipe. Instead it starts the form as if there is
no ingredient.
I have tried various way and search the web for similar case but cant
find anything
Here is the form I use:
class IngredientListTemplate(Form):
"""
Template to add a new ingredient to a recipe
The 2 input are prefilled with the date from the query_factory
"""
category = QuerySelectField(query_factory=categorylist,
get_label=u'category', allow_blank=False)
ingredient = QuerySelectField(query_factory=ingredientlist,
get_label=u'ingredient', allow_blank=False)
#it removes the CSRF token missing error
def __init__(self, *args, **kwargs):
kwargs['csrf_enabled'] = False
super(IngredientListTemplate, self).__init__(*args, **kwargs)
class IngredientListForm(Form):
"""
Display the template
"""
ingredient_list = FieldList(FormField(IngredientListTemplate),
min_entries=1)
and the function I use to edit the recipe:
@mod.route('/ingredients_selection/<int:recipe_id>', methods=['GET',
'POST'])
def ingredient_selection(recipe_id):
"""
This is where you can select ingredients for a specific recipe
It also stores them in a database for that recipe.
It is accessed from /ingredients
"""
recipe = Recipeingredients.query.get(recipe_id)
print recipe.ingredient_id #returns [5, 6, 9, 10, 19, 21, 23] being
the id of each ingredients
form = IngredientListForm()
form.ingredient_list[0]['ingredient'].data=9 # this is my attempt
at trying to force a some data in the form
form.ingredient_list[0]['category'].data=1 # but it doesn't work.
I still end up with the default form
print form.data
# Section to refresh the ingredient when the category is changed
# if filters the ingredient based on the chosen category
# it is buggy and sometime it just won't work :(
if request.is_xhr:
## each line is numbered like this : ingredient_list-x-category
## x is the number of the line (each time you press clone it
will step
key = list(request.args.keys())[0] ## extract the line number
form_cat = request.args.get(key) ## get the value chosen (ie the
category id)
## Then query the ingredient and filter them by the category
selected
ingredients = Ingredientlist.query.filter_by(category_id=form_cat)
result = dict()
for ingredient in ingredients:
#print ingredient.ingredient
result[ingredient.ingredient] = ingredient.id
print result
return jsonify(result=result) # ingredients name and id for the
clicked category
# Section when we validate the form
if form.validate_on_submit():
print form.data
"""
Return a dictionary in the form of :
{'ingredient_list':
[
{'category': Flour - Grain, 'ingredient': ingredient_id },
{'category': Flour - Grain, 'ingredient': ingredient_id }
]
}
"""
#remove potential duplicate
ingredients_list=[]
for i in form.data['ingredient_list']:
if i not in ingredients_list:
ingredients_list.append(i)
#Let's store it in the Recipeingredients table
#We are just interested in storing the id of the ingredients
since the rest can
#be deducted from there.
# First we create the list of ingredient id
ing_list = []
for ingredient in ingredients_list:
ing_list.append(ingredient['ingredient'])
# Then we update the table with the updated ingredient
recipe.ingredient_id = ing_list
db.session.add(recipe)
db.session.commit()
# Now we can return to displaying the ingredients list
return redirect(url_for('.ingredients_display',recipe_id=recipe_id))
# Default display of the form
## TODO: We need to read the recipe being edited and preload it with
## pre existing ingredient if they exist or a "clear form" if not
return render_template("ingredients_selection.html",
action = 'Save',
title='List of Ingredients',
form = form,
recipe_id=recipe_id
)
and finally the template I use
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% include "mybase.html" %}
{% block content %}
<h1>{{ title }}</h1>
<p>In this section you will select the ingredient that will be the base
of the recipe</p>
<form class="form form-inline" method="post" name="list_of_ingredient">
{{ form.hidden_tag() }}
<fieldset class="clonable">
{% for ingredient in form.ingredient_list %}
{{ wtf.form_field(ingredient.category) }}
{{ wtf.form_field(ingredient.ingredient) }}
{% endfor %}
<br/>
</fieldset>
<div id="formbuttons" class="regrow">
<input type="button" class="button" id="clonetrigger"
value="Clone" />
<input type="submit" value="Submit" />
</div>
</form>
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="text/javascript">
$(document).ready(function(){
$('form').click(function(event) {
myid = event.target.id; //get the id of the event
if (myid.indexOf("category") !=-1) { //Are we clicking
on a category (if not ignore)
console.log(myid);
console.log("PPMT");
ing_target = myid.replace('-category','-ingredient');
//Lets pack the XHR request
$('form').on('change','#'+myid,function() {
$.getJSON("/ingredients/ingredients_selection/{{
recipe_id }}",
$('#'+myid),
function(data) {
//$('#ingredient_list-1-ingredient').empty();
$('#'+ing_target).empty();
$.each( data.result, function( key, val ) {
//$('#ingredient_list-1-ingredient').append($('<option>', {'value': val,
'text': key }));
$('#'+ing_target).append($('<option>',
{'value': val, 'text': key }));
});
}
);
});
};
});
//Section for the cloning
$("#clonetrigger").on('click',function(){
var yourclass=".clonable"; //The class you have used in
your form
var clonecount = $(yourclass).length-1; //how many clones
do we already have? was 0 but changed it to -1 to avoid jumping from 0 to 2
var newid = Number(clonecount) + 1; //Id of the new clone
//alert( newid);
$(yourclass+":first").fieldclone({//Clone the original element
newid_: newid, //Id of the new clone, (you can pass
your own if you want)
target_: $("#formbuttons"), //where do we insert the
clone? (target element)
insert_: "before", //where do we insert the clone?
(after/before/append/prepend...)
limit_: 9 //Maximum Number of Clones
});
});
});
(function($) {
$.fn.fieldclone = function(options) {
//==> Options <==//
var settings = {
newid_ : 0,
target_: $(this),
insert_: "before",
limit_: 0
};
if (options) $.extend(settings, options);
//alert(settings.newid_);
if( (settings.newid_ <= (settings.limit_)) ||
(settings.limit_==0) ){ //Check the limit to see if we can clone
//==> Clone <==//
var fieldclone = $(this).clone();
//alert (fieldclone);
var node = $(this)[0].nodeName;
var classes = $(this).attr("class");
//==> Increment every input id <==//
var srcid = 0;
$(fieldclone).find(':input').each(function(){
var s = $(this).attr("name");
$(this).attr("name",
s.replace(eval('/-'+srcid+'/ig'),'-'+settings.newid_));
$(this).attr("id",
s.replace(eval('/-'+srcid+'/ig'),'-'+settings.newid_));
//alert(
s.replace(eval('/-'+srcid+'/ig'),'-'+settings.newid_));
});
//==> Locate Target Id <==//
var targetid = $(settings.target_).attr("id");
//alert (targetid);
if(targetid.length<=0){
targetid = "clonetarget";
$(settings.target_).attr("id",targetid);
}
//==> Insert Clone <==//
var newhtml = $(fieldclone).html().replace(/\n/gi,"");
newhtml = '<'+node+'
class="'+classes+'">'+newhtml+'</'+node+'>';
//alert(newhtml);
eval("var insertCall =
$('#"+targetid+"')."+settings.insert_+"(newhtml)");
}
};
})(jQuery);
</script>
{% endblock %}
I know it is a lot of code but I would appreciate if someone could help
me understand how I could achieve what I want.
Thanks for reading so far.
Philippe
More information about the Flask
mailing list