Wtforms 结合 Flask-Bootstrap 使用记录

这边主要记录,<<flask-web开发>>没有介绍的内容

wtf.quick_form 的使用

看一下其内部实现的部分代码

1
2
3
4
5
6
7
8
9
10
{# valid form types are "basic", "inline" and "horizontal" #}
{% macro quick_form(form,
action="",
method="post",
extra_classes=None,
role="form",
form_type="basic",
horizontal_columns=('lg', 2, 10),
enctype=None,
button_map={}) %}

这是各个参数的简介

1
2
3
4
5
6
7
8
9
10
Parameters:	
form – 要渲染的表单
method – 表单 method 的变量.
extra_classes – 表单的类.
role – <form> role attribute.
form_type – 表单样式有: basic, inline or horizontal.
horizontal_columns – 当使用horizontal 样式时有效,其是一个含有三个元素的元组 (column-type, left-column-size, right-colum-size).
enctype – <form> enctype attribute. If None, will automatically be set to multipart/form-data if a FileField is present in the form.
button_map – 设置按钮样式有: primary, danger or success等样式.
id – The <form> id attribute.

form_type

form_type – One of basic, inline or horizontal. See the Bootstrap docs for details on different form layouts.

  • basic
  • inline
  • horizontal

form_type 有三种样式,分别为basic, inlinehorizontal

button_map

修改按钮样式

原样式

1
{{ wtf.quick_form(form,form_type='horizontal') }}


修改后

1
{{ wtf.quick_form(form,form_type='horizontal',button_map={'submit':'primary'}) }}

综合 修改bootstrap模板实现一个漂亮的表单

来自于教程
上效果图:

  • 复制wtf.html一份,并改名为_wtf4.html
  • 覆盖以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    {# Adapte for Bootstrap4, by Kevin ZQ (17197602@qq.com) #}

    {% macro form_errors(form, hiddens=True) %}
    {%- if form.errors %}
    {%- for fieldname, errors in form.errors.items() %}
    {%- if bootstrap_is_hidden_field(form[fieldname]) and hiddens or
    not bootstrap_is_hidden_field(form[fieldname]) and hiddens != 'only' %}
    {%- for error in errors %}
    <p class="error">{{error}}</p>
    {%- endfor %}
    {%- endif %}
    {%- endfor %}
    {%- endif %}
    {%- endmacro %}

    {% macro _hz_form_wrap(horizontal_columns, form_type, add_group=False, required=False) %}
    {% if form_type == "horizontal" %}
    {% if add_group %}<div class="input-group{% if required %} required{% endif %}">{% endif %}
    {# Bootstrap4: <div class="col-md-4 offset-md-4"> #}
    <div class="col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}} offset-{{horizontal_columns[0]}}-{{horizontal_columns[1]}}">
    {% endif %}
    {{caller()}}

    {% if form_type == "horizontal" %}
    {% if add_group %}</div>{% endif %}
    </div>
    {% endif %}
    {% endmacro %}

    {% macro form_field(field,
    form_type="basic",
    horizontal_columns=('lg', 2, 10),
    button_map={},
    fa_addon="") %}

    {# this is a workaround hack for the more straightforward-code of just passing required=required parameter. older versions of wtforms do not have
    the necessary fix for required=False attributes, but will also not set the required flag in the first place. we skirt the issue using the code below #}
    {% if field.flags.required and not required in kwargs %}
    {% set kwargs = dict(required=True, **kwargs) %}
    {% endif %}

    {% if field.widget.input_type == 'checkbox' %}
    {% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
    <div class="checkbox">
    <label>
    {{field()|safe}} {{field.label.text|safe}}
    </label>
    </div>
    {% endcall %}
    {%- elif field.type == 'RadioField' -%}
    {# note: A cleaner solution would be rendering depending on the widget,
    this is just a hack for now, until I can think of something better #}
    {% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
    {% for item in field -%}
    <div class="radio">
    <label>
    {{item|safe}} {{item.label.text|safe}}
    </label>
    </div>
    {% endfor %}
    {% endcall %}
    {%- elif field.type == 'SubmitField' -%}
    {# deal with jinja scoping issues? #}
    {% set field_kwargs = kwargs %}

    {# note: same issue as above - should check widget, not field type #}
    {% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
    {{field(class='btn btn-%s' % button_map.get(field.name, 'default'),
    **field_kwargs)}}
    {% endcall %}
    {%- elif field.type == 'FormField' -%}
    {# note: FormFields are tricky to get right and complex setups requiring
    these are probably beyond the scope of what this macro tries to do.
    the code below ensures that things don't break horribly if we run into
    one, but does not try too hard to get things pretty. #}
    <fieldset>
    <legend>{{field.label}}</legend>
    {%- for subfield in field %}
    {% if not bootstrap_is_hidden_field(subfield) -%}
    {{ form_field(subfield,
    form_type=form_type,
    horizontal_columns=horizontal_columns,
    button_map=button_map) }}
    {%- endif %}
    {%- endfor %}
    </fieldset>
    {% else -%}
    <div class="form-group">
    <div class="input-group {% if field.errors %} has-danger{% endif -%}
    {%- if field.flags.required %} required{% endif -%}
    ">
    {%- if fa_addon %}
    <span class="input-group-addon"><i class="fa {{ fa_addon }} fa-fw"></i></span>
    {% endif %}
    {%- if form_type == "inline" %}
    {{field.label(class="sr-only")|safe}}
    {% if field.type == 'FileField' %}
    {{field(**kwargs)|safe}}
    {% else %}
    {{field(class="form-control", **kwargs)|safe}}
    {% endif %}
    {% elif form_type == "horizontal" %}
    {{field.label(class="control-label " + (
    " col-%s-%s" % horizontal_columns[0:2]
    ))|safe}}
    <div class=" col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}">
    {% if field.type == 'FileField' %}
    {{field(**kwargs)|safe}}
    {% else %}
    {{field(class="form-control", **kwargs)|safe}}
    {% endif %}
    </div>
    {%- if field.errors %}
    {%- for error in field.errors %}
    {% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
    <p class="help-block error"><small>{{error}}</small></p>
    {% endcall %}
    {%- endfor %}
    {%- elif field.description -%}
    {% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
    <p class="help-block grey"><small>{{field.description|safe}}</small></p>
    {% endcall %}
    {%- endif %}
    {%- else -%}
    {{field.label(class="control-label")|safe}}
    {% if field.type == 'FileField' %}
    {{field(**kwargs)|safe}}
    {% else %}
    {{field(class="form-control", **kwargs)|safe}}
    {% endif %}
    </div>
    {%- if field.errors %}
    {%- for error in field.errors %}
    <small class="form-text text-danger">{{error}}</small>
    {%- endfor %}
    {%- elif field.description -%}
    <small class="form-text text-muted">{{field.description|safe}}</small>
    {%- endif %}
    {%- endif %}
    </div>
    {% endif %}

    {% endmacro %}

    {# valid form types are "basic", "inline" and "horizontal" #}
    {% macro quick_form(form,
    action="",
    method="post",
    extra_classes=None,
    role="form",
    form_type="basic",
    horizontal_columns=('lg', 2, 10),
    enctype=None,
    button_map={},
    id="") %}
    {#-
    action="" is what we want, from http://www.ietf.org/rfc/rfc2396.txt:

    4.2. Same-document References

    A URI reference that does not contain a URI is a reference to the
    current document. In other words, an empty URI reference within a
    document is interpreted as a reference to the start of that document,
    and a reference containing only a fragment identifier is a reference
    to the identified fragment of that document. Traversal of such a
    reference should not result in an additional retrieval action.
    However, if the URI reference occurs in a context that is always
    intended to result in a new request, as in the case of HTML's FORM
    element, then an empty URI reference represents the base URI of the
    current document and should be replaced by that URI when transformed
    into a request.

    -#}
    {#- if any file fields are inside the form and enctype is automatic, adjust
    if file fields are found. could really use the equalto test of jinja2
    here, but latter is not available until 2.8

    warning: the code below is guaranteed to make you cry =(
    #}
    {%- set _enctype = [] %}
    {%- if enctype is none -%}
    {%- for field in form %}
    {%- if field.type == 'FileField' %}
    {#- for loops come with a fairly watertight scope, so this list-hack is
    used to be able to set values outside of it #}
    {%- set _ = _enctype.append('multipart/form-data') -%}
    {%- endif %}
    {%- endfor %}
    {%- else %}
    {% set _ = _enctype.append(enctype) %}
    {%- endif %}
    <form
    {%- if action != None %} action="{{action}}"{% endif -%}
    {%- if id %} id="{{id}}"{% endif -%}
    {%- if method %} method="{{method}}"{% endif %}
    class="form {%- if extra_classes %} {{extra_classes}}{% endif -%}
    {%- if form_type == "horizontal" %}form-horizontal
    {%- elif form_type == "inline" %}form-inline
    {%- endif -%}
    "
    {%- if _enctype[0] %} enctype="{{_enctype[0]}}"{% endif -%}
    {%- if role %} role="{{role}}"{% endif -%}
    >
    {{ form.hidden_tag() }}
    {{ form_errors(form, hiddens='only') }}

    {%- for field in form %}
    {% if not bootstrap_is_hidden_field(field) -%}
    {{ form_field(field,
    form_type=form_type,
    horizontal_columns=horizontal_columns,
    button_map=button_map,
    fa_addon=form.fa_addon[field.name]) }}
    {%- endif %}
    {%- endfor %}

    </form>
    {%- endmacro %}
  • 在登录表单中添加font-awesome图标
    图标可以在此查找

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class LoginForm(FlaskForm):
    no = IntegerField('', validators=[DataRequired()],render_kw={'placeholder':u'在这里填写编号'})
    password = PasswordField("", validators=[DataRequired()],render_kw={'placeholder':u'在这里填写密码'})
    submit = SubmitField(u'登录')

    # FontAwesome css, show icon as prefix of fields
    fa_addon = {
    'no': 'fa-user-o',
    'password': 'fa-key',
    }
  • 修改模板,使用_wtf4.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {% extends "base.html" %}
    {% import "bootstrap/_wtf4.html" as wtf %}
    {% block title %}Login{% endblock %}

    {% block page_content %}
    <div class="row">
    <div class="col-md-4">

    </div>
    <div class="col-md-4">
    {{ wtf.quick_form(form,form_type='basic',button_map={'submit':'primary'}) }}
    </div>
    </div>

    {% endblock %}

注意要引入font-awesome.css否则显示不了图标

1
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet">

本文标题:Wtforms 结合 Flask-Bootstrap 使用记录

文章作者:定。

发布时间:2017年6月6日 - 01时06分

本文字数:6,168字

原始链接:http://cocofe.cn/2017/06/06/wtforms 结合 Flask-Bootstrap 使用记录/

许可协议: Attribution-NonCommercial 4.0

转载请保留以上信息。