Python flask 学习笔记(三)

一、Web 表单

本小节主要讲解了以下内容:

  • WTForms 表单类
  • 常见的 HTML 标准字段
  • 表单的渲染

test_form.py 这里的 render_template 可以到 form.html 也可以到 form2.html

from flask import Flask, render_template

from forms import LoginForm

app = Flask(__name__)

@app.route('/')
def login():
    form = LoginForm()
    return render_template('form2.html', form=form)


app.config['SECRET_KEY'] = 'A RANDOM STRING'

if __name__ == '__main__':
    app.run(debug=True)

forms.py

"""
表单类
参考文档:http://wtforms.readthedocs.io/en/latest/

字段类型            说明
StringField         文本字段
TextAreaField       多行文本字段
PasswordField       密码文本字段
HiddenField         隐藏文本字段
DateField           文本字段,值为datetime.date格式
DateTimeField       文本字段,值为datetime.datetime格式
IntegerField        文本字段,值为整数
DecimalField        文本字段,值为decimal.Decimal
FloatField          文本字段,值为浮点数
BooleanField        复选框,值为True和False
RadioField          一组单选框
SelectField         下拉列表
SelectMultipleField 下拉列表,可选择多个值
FileField           文件上传字段
SubmitField         表单提交按钮
FormField           把表单作为字段嵌入另一个表单
FieldList           一组指定类型的字段

"""
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField, RadioField, \
TextAreaField, DateField, BooleanField
from wtforms.validators import DataRequired
from wtforms.widgets import CheckboxInput, PasswordInput

# 表单类
class LoginForm(FlaskForm):
    # DataRequired 设置必需填写;render_kw 为对应标签的属性设置参数
    username = StringField(label='用户名', validators=[DataRequired()], 
        description="请输入用户名",
        render_kw={"required": "required", "class": "这是选择器"}
        )
    password = StringField('密码', validators=[DataRequired()], widget=PasswordInput())
    language = RadioField('编程语言',choices=[('cpp','C++'), ('py','Python')])
    code = TextAreaField('代码')
    date = DateField('日期', render_kw={"type" : "date"})
    is_checked = BooleanField('是否已经通过验证')
    submit = SubmitField('登录')

form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form</title>
</head>
<body>
    <form>
        <div>
            {# 通过 (id="id-username") 可以设置对应标签的属性参数 #}
            {{ form.username.label }} : {{ form.username(id="id-username")}}
            <small>{{ form.username.description }}</small>
        </div>
        <div>
            {{ form.password.label }} : {{ form.password }}
        </div>
        <div>
            {{ form.language.label }}
            {% for radio in form.language %}
            <label>{{ radio }} {{ radio.label.text }}</label>
            {% endfor %}
        </div>
        <div>
            {{ form.code.label }} : {{ form.code }}
        </div>
        <div>
            {{ form.date.label }} : {{ form.date }}
        </div>
        <div>
            {{ form.is_checked.label }} : {{ form.is_checked }}
        </div>
        <div>
            {{ form.submit.label }} : {{ form.submit }}
            {# 注意这里的csrf #}
            {{ form.csrf_token }}
        </div>
    </form>
</body>
</html>

form2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form2</title>
</head>
<body>
    <form action="." method="get" accept-charset="utf-8">
        {% for field in form %}
            {# 如果是隐藏表单域,则不显示label #}
            {% if field.type in ['CSRFTokenField', 'HiddenField'] %}
                {{ field }}
            {% else %}
            <div>
                {{ field.label }} : {{ field }}
            </div>
            {% endif %}
        {% endfor %}
    </form>
</body>
</html>

二、表单的处理

  • Form 内置函数的使用
  • POST
  • GET
  • FILES

直观上来说 GET 方式:
好处:分享链接后,别人打开的和你打开的一样
坏处:你的值在链接里,不够隐私

GET POST 方式异同:

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被Bookmark,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST么有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。

test_regitst.py

import os

from flask import Flask, render_template, request

from forms import RegistForm, UploadForm

app = Flask(__name__)


# 文件上传目录 os.path.dirname(__file__) 指代当前文件路径,后面加个 medias 指的是,当前路径下的 medias 目录
UPLOAD_PATH = os.path.join(os.path.dirname(__file__), 'medias')


@app.route('/regist/', methods=['GET', 'POST'])
def regist():
    form = RegistForm()
    if form.validate_on_submit():
        data = form.data
        print(data)
    return render_template('regist.html', form=form)


@app.route('/upload/', methods=['GET', 'POST'])
def upload():
    form = UploadForm()
    if form.validate_on_submit():
        print(request.files['image'])
        image = form.data['image']
        if image:
            print(image.filename)
            filename = os.path.join(UPLOAD_PATH, image.filename)
            print(filename)
            image.save(filename)
            flash("文件上传成功")
            return 'success!'
        else:
            return 'no file'
    else:
        print(form.errors)
    return render_template('upload.html', form=form)


app.config['SECRET_KEY'] = 'A RANDOM STRING'

if __name__ == '__main__':
    app.run(debug=True)

forms.py

需要注意的是 validators 方式是进行后台的验证,而设置 required 的方式或者使用 JavaScript 的方式,只是前端的方式,前端的方式是可以绕过去的。

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField, RadioField, \
TextAreaField, DateField, BooleanField, PasswordField, FileField
from wtforms.validators import DataRequired
from wtforms.widgets import CheckboxInput, PasswordInput

from validator import passwordValid

class LoginForm(FlaskForm):
    username = StringField(label='用户名', validators=[DataRequired()], 
        description="请输入用户名",
        render_kw={"required": "required", "class": "这是选择器"}
        )
    password = StringField('密码', validators=[DataRequired()], widget=PasswordInput())
    language = RadioField('编程语言',choices=[('cpp','C++'), ('py','Python')])
    code = TextAreaField('代码')
    date = DateField('日期', render_kw={"type" : "date"})
    is_checked = BooleanField('是否已经通过验证')
    submit = SubmitField('登录')


class RegistForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired()],
        render_kw={'required': 'required', 'placeholder': '请输入用户名'},
        description='输入邮箱进行注册'
    )
    password = PasswordField('密码', validators=[DataRequired('请输入密码'), passwordValid])
    language = RadioField('编程语言', choices=[('cpp','C++'), ('py','Python')])



class UploadForm(FlaskForm):
        image = FileField('文件上传', validators=[DataRequired()],
            render_kw={'required': 'required', 'class': 'form-control'}
        )

validator.py

from wtforms.validators import ValidationError

def passwordValid(form, field):
    password = field.data
    if len(password) != 6:
        raise ValidationError('密码必须是6位')
    if not password.isdigit():
        raise ValidationError('密码必须是数字')
    return password

regist.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>regist</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css', _external=True)}}">
</head>
<body>
    <div class="container">
        {% for message in get_flashed_messages() %}
        <div class="alert alert-warning">
            <button type="button" class="close" data-dismiss='alert'>&times;</button>
            {{ message }}
        </div>
        {% endfor %}
        <div class="col-md-6">
            <form method="POST" action=".">
                <hr>
                {{ form.username.label }} {{ form.username }}
                <hr>
                {{ form.password.label }} {{ form.password }}
                <hr>
                {{ form.language.label }} {{ form.language }}
                <hr>
                <b>
                {% if form.password.errors %}
                {{ form.password.errors }}
                {% endif %}
                </b>
                <hr>
                {{ form.csrf_token }}
                <button type="submit">提交</button>
            </form>
        </div>
    </div>
    <script type="text/javascript" src="{{ url_for('static', filename='jquery.1.10.2.min.js', _external=True) }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js', _external=True) }}"></script>
</body>
</html>

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>upload</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css', _external=True) }}">
</head>
<body>
    <div class="container">
        <div class="col-md-6">
            <form action=".", method="post", enctype="multipart/form-data">
                <hr>
                {{ form.image.label }}
                <hr>
                {{ form.image }}
                <hr>
                {{ form.csrf_token }}
                <hr>
                <button type="submit">上传</button>
            </form>
        </div>
    </div>
    <script type="text/javascript" src="{{ url_for('static', filename='jquery.1.10.2.min.js', _external=True)}}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js', _external=True) }}"></script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容