Howto:使用Flask在WTForms中动态生成CSRF-Token [英] Howto: Dynamically generate CSRF-Token in WTForms with Flask

查看:325
本文介绍了Howto:使用Flask在WTForms中动态生成CSRF-Token的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  bananas = FieldList(FormField(BananaForm))

我有一个水果表单,它有一个用于香蕉的FieldList对象。

在前端中,最初我将其中一个字段添加到FieldList中。
$ b $ p $ form.append_entry()



<现在用Javascript,我设法创建函数,可以动态地添加(加按钮)或删除(减去按钮)可以填充信息的BananaForm字段的数量。

< FielstList自动为其所有字段创建id。所以要用js做动态添加,我复制了HTML代码并设置了字段id + = 1,如:

第一个字段:

 < tr> 
< td>< input id =bananas-0-originCountrytype =text/>< / td>
< / tr>

带有+ = 1的复制字段:

 < TR> 
< td>< input id =bananas-1-originCountrytype =text/>< / td>
< / tr>

当我这样命名并提交表单时,WTForms会自动识别添加的字段后端(工作正常)。

到目前为止,这是我的问题:
为了使表单有效,我必须添加CSRF字段到每个WTForm。在Jinja模板中,我可以这样做:

  {{form.hidden_​​tag()}} 

这应该是一个标准的用例和形式和瓶。我希望我的描述是可以理解的,如果不是,请让我知道。任何帮助赞赏!

更新:这里是我的代码

JS函数

  function addBanana(){
//克隆并插入banana节点
var node = document.getElementById(fruitTable);
var trs = node.getElementsByTagName(tr);
var tr = trs [trs.length-2];
var tr2 = tr.cloneNode(true);
tr.parentNode.insertBefore(tr2,tr);

//为了增加标签和输入字段id
函数plusone(str){
return str.replace(
new RegExp( - (\
函数($ 0,$ 1){
var i = parseInt($ 1)+ 1;
return - + i + - ;
}
);
}

//更改输入
var inputs = tr.getElementsByTagName(input); (var i = 0; i< inputs.length; i ++){
inputs [i] .setAttribute(id,plusone(inputs [i] .getAttribute( ID)));

$ b $ var minusbutton =
['< td>',
'< button class =btntype =buttononClick =removeBanana ()>< i class =icon-black icon-minus>< / i>< / button>',
'< / td>'
] .join ( '\\\
');

//只追加第一个
//第二个自动复制减去按钮
if(trs.length< 6){
tr.innerHTML + = minusbutton



function removeBanana(){
var node = document.getElementById(fruitTable);
var trs = node.getElementsByTagName(tr);
var tr = trs [trs.length-2];
var trParent = tr.parentNode;
trParent.removeChild(tr);

$ / code>

Jinja Template:

 < form method =POSTaction =newsubmit> 
{{form.hidden_​​tag()}}
< table id =fruitTableclass =table>
{{render_field(form.description)}}
< tr>< td>< h3>香蕉< / h3>< / td>< / tr>
{%set counter = 0%}
{%for form.bananas%中的香蕉}
< tr>
{{banana.hidden_​​tag()}}
{%set counter = counter + 1%}
如果field.widget.input_type!='hidden'%}
{{render_field_oneline(field)}}
{%endfor%}
{%if counter> 1%}
< td>
< button class =btntype =buttononClick =removeBanana()>< i class =icon-black icon-minus>< / i>< / button> ;
< / td>
{%endif%}
< / tr>
{%endfor%}
< tr>< td>< / td>< td>< button class =btntype =buttononClick =addBanana() >< i class =icon-black icon-plus>< / i>< / button>< / td>< / tr>
< / table>
< input class =btn btn-primarystyle =margin-left:300px;type =submitvalue =Submit/>
< / form>

Jinja模板宏:

  {%macro render_field_oneline(field)%} 
< td> {{field.label}}< / td>
< td> {{field(** kwargs)| safe}}
{%if field.errors%}
< ul class = errors>
{%for field.errors%}
< li> {{error}}< / li>
{%endfor%}
< / ul>
{%endif%}
< / td>
{%endmacro%}

{%macro render_field(field)%}
< tr>
{{render_field_oneline(field)}}
< / tr>
{%endmacro%}


解决方案

它是如何工作的:

CSRF标签可以简单地复制。该ID必须相应地更改和增加,但散列可能保持不变。



我不认为有可能拥有许多具有相同CSRF-Tag散列的字段,但实际上是这样!


I have a fruits form that has one FieldList object for the bananas:

bananas = FieldList(FormField(BananaForm))

In the frontend, initially, I add one of those fields to the FieldList

form.append_entry()

Now with Javascript I managed to create functions, that can dynamically add (plus button) or remove (minus button) the number of BananaForm fields that can be filled with information.

FielstList automatically creates ids for all of its fields. So to do dynamical adding with js, I duplicate the HTML code and set the field id += 1, like:

first field:

<tr>
  <td><input id="bananas-0-originCountry" type="text" /></td>
</tr>

duplicated field with += 1:

<tr>
  <td><input id="bananas-1-originCountry" type="text" /></td>
</tr>

When I name them accordingly like this and submitting the form, WTForms will automatically recognize the added fields in the backend (works fine).

So far so good, but here is my problem: For a form to be valid, I have to add CSRF-fields to every WTForm. In the Jinja template I do this with:

{{ form.hidden_tag() }}

However, when I just copy the HTML with my js function, I'm missing the CSRF-fields (because until submitted, the backend form object doesn't know about the added FormFields). So how can I generate these CSRF-fields dynamically? (An Ajax Request? If yes, how?)

This should be a standard use case with forms and flask. I hope my description was understandable, if not please let me know. Any help appreciated!

UPDATE: Here is my code

JS-functions

function addBanana(){
    // clone and insert banana node
    var node = document.getElementById("fruitTable");
    var trs = node.getElementsByTagName("tr");
    var tr = trs[trs.length-2];
    var tr2 = tr.cloneNode(true);
    tr.parentNode.insertBefore(tr2, tr);

    // in order to increment label and input field ids
    function plusone(str){
        return str.replace(
            new RegExp("-(\\d+)-", "gi"),
            function($0, $1){
                var i = parseInt($1) + 1;
                return "-" + i + "-";
            }
        );
    }

    // change inputs
    var inputs = tr.getElementsByTagName("input");

    for (var i = 0; i < inputs.length; i++){
        inputs[i].setAttribute("id", plusone(inputs[i].getAttribute("id")));
    }

    var minusbutton = 
        ['<td>',
        '<button class="btn" type="button" onClick="removeBanana()"><i class="icon-black icon-minus"></i></button>',
        '</td>'
        ].join('\n');

    // only append at the first add
    // second add automatically copies minus button
    if (trs.length < 6){
        tr.innerHTML += minusbutton
    }
}

function removeBanana(){
    var node = document.getElementById("fruitTable");
    var trs = node.getElementsByTagName("tr");
    var tr = trs[trs.length-2];
    var trParent = tr.parentNode;
    trParent.removeChild(tr);
}

Jinja Template:

<form method="POST" action="newsubmit">
  {{ form.hidden_tag() }}
  <table id="fruitTable" class="table">
    {{ render_field(form.description) }}
    <tr><td><h3>Bananas</h3></td></tr>
    {% set counter = 0 %}
    {% for banana in form.bananas %} 
      <tr>
        {{ banana.hidden_tag() }}
        {% set counter = counter + 1%}
        {% for field in banana if field.widget.input_type != 'hidden' %}
          {{ render_field_oneline(field) }}
        {% endfor %}
        {% if counter > 1 %} 
          <td>
            <button class="btn" type="button" onClick="removeBanana()"><i class="icon-black icon-minus"></i></button>
          </td>
        {% endif  %} 
      </tr>
    {% endfor %}
      <tr><td></td><td><button class="btn" type="button" onClick="addBanana()"><i class="icon-black icon-plus"></i></button></td></tr>
  </table>
<input class="btn btn-primary" style="margin-left:300px;"type="submit" value="Submit" />
</form>

Jinja Template Macros:

{% macro render_field_oneline(field) %}
<td>{{ field.label }}</td>
<td>{{ field(**kwargs)|safe }}
  {% if field.errors %}
  <ul class=errors>
    {% for error in field.errors %}
    <li>{{ error }}</li>
    {% endfor %}
  </ul>
  {% endif %}
</td>
{% endmacro %}

{% macro render_field(field) %}
<tr>
  {{ render_field_oneline(field) }} 
</tr>
{% endmacro %}

解决方案

I discovered how it works:

The CSRF-Tag can simply be copied. The id must be changed and incremented accordingly, but the hash may stay the same.

I didn't think it was possible to have many Fields with the same CSRF-Tag hash, but it actually does!

这篇关于Howto:使用Flask在WTForms中动态生成CSRF-Token的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆