[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