Flask 第三方組件之 WTForms

簡介

WTForms是一個支持多個web框架的form組件,主要用于對用戶請求數據進行驗證。
安裝: pip3 install wtforms

用戶登錄注冊示例

1. 用戶登錄

當用戶登錄時候,需要對用戶提交的用戶名和密碼進行多種格式校驗。如:

  • 用戶不能為空;用戶長度必須大于6;
  • 密碼不能為空;密碼長度必須大于12;密碼必須包含 字母、數字、特殊字符等(自定義正則);
############################## app.py #############################
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core    # 字段大部分都在core html5 simple 中
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass LoginForm(Form):name = simple.StringField(label='用戶名',     # 顯示名稱validators=[       # 校驗validators.DataRequired(message='用戶名不能為空.'),validators.Length(min=6, max=18, message='用戶名長度必須大于%(min)d且小于%(max)d')],widget=widgets.TextInput(),           # 插件render_kw={'class': 'form-control'}   # 渲染)pwd = simple.PasswordField(label='密碼',validators=[validators.DataRequired(message='密碼不能為空.'),validators.Length(min=8, message='用戶名長度必須大于%(min)d'),validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",message='密碼至少8個字符,至少1個大寫字母,1個小寫字母,1個數字和1個特殊字符')],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'GET':form = LoginForm()            # 實例化return render_template('login.html', form=form)else:form = LoginForm(formdata=request.form)           # 校驗if form.validate():print('用戶提交數據通過格式驗證,提交的值為:', form.data)else:print(form.errors)return render_template('login.html', form=form)if __name__ == '__main__':app.run()
############################## login.html #############################
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>登錄</h1>
<form method="post"><!--<input type="text" name="name">--><p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p><!--<input type="password" name="pwd">--><p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p><input type="submit" value="提交">
</form>
</body>
</html>

2. 用戶注冊

注冊頁面需要讓用戶輸入:用戶名、密碼、密碼重復、性別、愛好等。

############################## app.py #############################
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass RegisterForm(Form):name = simple.StringField(label='用戶名',validators=[validators.DataRequired()],widget=widgets.TextInput(),render_kw={'class': 'form-control'},default='alex')pwd = simple.PasswordField(label='密碼',validators=[validators.DataRequired(message='密碼不能為空.')],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})pwd_confirm = simple.PasswordField(label='重復密碼',validators=[validators.DataRequired(message='重復密碼不能為空.'),validators.EqualTo('pwd', message="兩次密碼輸入不一致")],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})email = html5.EmailField(label='郵箱',validators=[validators.DataRequired(message='郵箱不能為空.'),validators.Email(message='郵箱格式錯誤')],widget=widgets.TextInput(input_type='email'),render_kw={'class': 'form-control'})gender = core.RadioField(label='性別',choices=((1, '男'),(2, '女'),),coerce=int   # 會自動轉化為int)city = core.SelectField(label='城市',choices=(('bj', '北京'),('sh', '上海'),))hobby = core.SelectMultipleField(label='愛好',choices=((1, '籃球'),(2, '足球'),),coerce=int)favor = core.SelectMultipleField(label='喜好',choices=((1, '籃球'),(2, '足球'),),widget=widgets.ListWidget(prefix_label=False),option_widget=widgets.CheckboxInput(),coerce=int,default=[1, 2])def __init__(self, *args, **kwargs):super(RegisterForm, self).__init__(*args, **kwargs)self.favor.choices = ((1, '籃球'), (2, '足球'), (3, '羽毛球'))def validate_pwd_confirm(self, field):  # 鉤子函數,filed 代表要檢驗的字段,self 代表所有的數據"""自定義pwd_confirm字段規則,例:與pwd字段是否一致:param field: :return: """# 最開始初始化時,self.data中已經有所有的值if field.data != self.data['pwd']:# raise validators.ValidationError("密碼不一致") # 繼續后續驗證,驗證的是本字段raise validators.StopValidation("密碼不一致")  # 不再繼續后續驗證,驗證的是本字段@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'GET':form = RegisterForm(data={'gender': 1})   # data 為form 傳值return render_template('register.html', form=form)else:form = RegisterForm(formdata=request.form)if form.validate():print('用戶提交數據通過格式驗證,提交的值為:', form.data)else:print(form.errors)return render_template('register.html', form=form)if __name__ == '__main__':app.run()
############################## login.html #############################
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>用戶注冊</h1>
<form method="post" novalidate style="padding:0  50px">{% for item in form %}<p>{{item.label}}: {{item}} {{item.errors[0] }}</p>{% endfor %}<input type="submit" value="提交">
</form>
</body>
</html>

3. meta

使用WTFrom 提供的 csrf_token,一般不怎么用

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect, session
from wtforms import Form
from wtforms.csrf.core import CSRF
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
from hashlib import md5app = Flask(__name__, template_folder='templates')
app.debug = Trueclass MyCSRF(CSRF):  # 繼承CSRF,實現自己的規則"""Generate a CSRF token based on the user's IP. I am probably not verysecure, so don't use me."""def setup_form(self, form):self.csrf_context = form.meta.csrf_context()self.csrf_secret = form.meta.csrf_secretreturn super(MyCSRF, self).setup_form(form)def generate_csrf_token(self, csrf_token):gid = self.csrf_secret + self.csrf_contexttoken = md5(gid.encode('utf-8')).hexdigest()return tokendef validate_csrf_token(self, form, field):print(field.data, field.current_token)if field.data != field.current_token:raise ValueError('Invalid CSRF')class TestForm(Form):name = html5.EmailField(label='用戶名')pwd = simple.StringField(label='密碼')class Meta:# -- CSRF# 是否自動生成CSRF標簽csrf = True# 生成CSRF標簽namecsrf_field_name = 'csrf_token'# 自動生成標簽的值,加密用的csrf_secretcsrf_secret = 'xxxxxx'# 自動生成標簽的值,加密用的csrf_contextcsrf_context = lambda x: request.url# 生成和比較csrf標簽csrf_class = MyCSRF# -- i18n# 是否支持本地化# locales = Falselocales = ('zh', 'en')# 是否對本地化進行緩存cache_translations = True# 保存本地化緩存信息的字段translations_cache = {}@app.route('/index/', methods=['GET', 'POST'])
def index():if request.method == 'GET':form = TestForm()else:form = TestForm(formdata=request.form)if form.validate():print(form)return render_template('index.html', form=form)if __name__ == '__main__':app.run()

其他:

1. metaclass

第一階段

class MyType(type):def __init__(self, *args, **kwargs):print('MyType創建類',self)super(MyType, self).__init__(*args, **kwargs)def __call__(self, *args, **kwargs):obj = super(MyType, self).__call__(*args, **kwargs)print('類創建對象', self, obj)return objclass Foo(object,metaclass=MyType):user = 'flepeng'age = 18obj = Foo()

第二階段

class MyType(type):def __init__(self, *args, **kwargs):super(MyType, self).__init__(*args, **kwargs)def __call__(cls, *args, **kwargs):v = dir(cls)obj = super(MyType, cls).__call__(*args, **kwargs)return objclass Foo(MyType('MyType', (object,), {})):user = 'wupeiqi'age = 18obj = Foo()

第三階段

class MyType(type):def __init__(self, *args, **kwargs):super(MyType, self).__init__(*args, **kwargs)def __call__(cls, *args, **kwargs):v = dir(cls)obj = super(MyType, cls).__call__(*args, **kwargs)return objdef with_metaclass(arg,base):return MyType('MyType', (base,), {})class Foo(with_metaclass(MyType,object)):user = 'wupeiqi'age = 18obj = Foo()

2. 實例化流程分析

# 源碼流程1. 執行type的 __call__ 方法,讀取字段到靜態字段 cls._unbound_fields 中; meta類讀取到cls._wtforms_meta中2. 執行構造方法a. 循環cls._unbound_fields中的字段,并執行字段的bind方法,然后將返回值添加到 self._fields[name] 中。即:_fields = {name: wtforms.fields.core.StringField(),}PS:由于字段中的__new__方法,實例化時:name = simple.StringField(label='用戶名'),創建的是UnboundField(cls, *args, **kwargs),當執行完bind之后,才變成執行 wtforms.fields.core.StringField()b. 循環_fields,為對象設置屬性for name, field in iteritems(self._fields):# Set all the fields to attributes so that they obscure the class# attributes with the same names.setattr(self, name, field)c. 執行process,為字段設置默認值:self.process(formdata, obj, data=data, **kwargs)優先級:obj,data,formdata;再循環執行每個字段的process方法,為每個字段設置值:for name, field, in iteritems(self._fields):if obj is not None and hasattr(obj, name):field.process(formdata, getattr(obj, name))elif name in kwargs:field.process(formdata, kwargs[name])else:field.process(formdata)執行每個字段的process方法,為字段的data和字段的raw_data賦值def process(self, formdata, data=unset_value):self.process_errors = []if data is unset_value:try:data = self.default()except TypeError:data = self.defaultself.object_data = datatry:self.process_data(data)except ValueError as e:self.process_errors.append(e.args[0])if formdata:try:if self.name in formdata:self.raw_data = formdata.getlist(self.name)else:self.raw_data = []self.process_formdata(self.raw_data)except ValueError as e:self.process_errors.append(e.args[0])try:for filter in self.filters:self.data = filter(self.data)except ValueError as e:self.process_errors.append(e.args[0])d. 頁面上執行print(form.name) 時,打印標簽因為執行了:字段的 __str__ 方法字符的 __call__ 方法self.meta.render_field(self, kwargs)def render_field(self, field, render_kw):other_kw = getattr(field, 'render_kw', None)if other_kw is not None:render_kw = dict(other_kw, **render_kw)return field.widget(field, **render_kw)執行字段的插件對象的 __call__ 方法,返回標簽字符串

3. 驗證流程分析

a. 執行form的validate方法,獲取鉤子方法def validate(self):extra = {}for name in self._fields:inline = getattr(self.__class__, 'validate_%s' % name, None)if inline is not None:extra[name] = [inline]return super(Form, self).validate(extra)b. 循環每一個字段,執行字段的 validate 方法進行校驗(參數傳遞了鉤子函數)def validate(self, extra_validators=None):self._errors = Nonesuccess = Truefor name, field in iteritems(self._fields):if extra_validators is not None and name in extra_validators:extra = extra_validators[name]else:extra = tuple()if not field.validate(self, extra):success = Falsereturn successc. 每個字段進行驗證時候字段的pre_validate 【預留的擴展】字段的_run_validation_chain,對正則和字段的鉤子函數進行校驗字段的post_validate【預留的擴展】
################################## class FormMeta ###############################
class FormMeta(type):# 創建類時,調用的方法def __init__(cls, name, bases, attrs):type.__init__(cls, name, bases, attrs)cls._unbound_fields = Nonecls._wtforms_meta = None# 實例化類時調用的方法def __call__(cls, *args, **kwargs):if cls._unbound_fields is None:fields = []for name in dir(cls):if not name.startswith('_'):unbound_field = getattr(cls, name)if hasattr(unbound_field, '_formfield'):   # 只要是繼承自File,_formfield = Truefields.append((name, unbound_field))   # 將字段添加至fields?fields.sort(key=lambda x: (x[1].creation_counter, x[0]))cls._unbound_fields = fields                       # _unbound_fields?=[字段對應的示例,]if cls._wtforms_meta is None:bases = []for mro_class in cls.__mro__:if 'Meta' in mro_class.__dict__:bases.append(mro_class.Meta)               # 將所有的Meta添加至bases中cls._wtforms_meta = type('Meta', tuple(bases), {}) # 創建一個新的Meta,并繼承自找到的Meta列表return type.__call__(cls, *args, **kwargs)             # 執行type的call方法,前面的都是派生的
################################## class Form ###############################
# class Form(BaseForm , classmate=FormMeta)
class Form(with_metaclass(FormMeta, BaseForm)):Meta = DefaultMetadef __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):meta_obj = self._wtforms_meta()if meta is not None and isinstance(meta, dict):meta_obj.update_values(meta)super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)for name, field in iteritems(self._fields):setattr(self, name, field)self.process(formdata, obj, data=data, **kwargs)def __setitem__(self, name, value):raise TypeError('Fields may not be added to Form instances, only classes.')def __delitem__(self, name):del self._fields[name]setattr(self, name, None)def __delattr__(self, name):if name in self._fields:self.__delitem__(name)else:unbound_field = getattr(self.__class__, name, None)if unbound_field is not None and hasattr(unbound_field, '_formfield'):setattr(self, name, None)else:super(Form, self).__delattr__(name)def validate(self):extra = {}for name in self._fields:inline = getattr(self.__class__, 'validate_%s' % name, None)if inline is not None:extra[name] = [inline]return super(Form, self).validate(extra)
################################## class BaseForm ###############################
class BaseForm(object):def __init__(self, fields, prefix='', meta=DefaultMeta()):if prefix and prefix[-1] not in '-_;:/.':prefix += '-'self.meta = metaself._prefix = prefixself._errors = Noneself._fields = OrderedDict()if hasattr(fields, 'items'):fields = fields.items()translations = self._get_translations()extra_fields = []if meta.csrf:self._csrf = meta.build_csrf(self)extra_fields.extend(self._csrf.setup_form(self))for name, unbound_field in itertools.chain(fields, extra_fields):options = dict(name=name, prefix=prefix, translations=translations)field = meta.bind_field(self, unbound_field, options)self._fields[name] = field

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454504.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454504.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454504.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器學習原理與算法(六) 支持向量機

版權聲明&#xff1a;本系列文章為博主原創文章&#xff0c;轉載請注明出處&#xff01;謝謝&#xff01; 本章索引&#xff1a; 從第3章的Logistic回歸算法開始&#xff0c;我們一直在討論分類問題。在各種不同的分類算法中&#xff0c;...&#xff0c;我們一直在討論如何分類…

讀《程序員的SQL金典》[2]--函數

一、數學函數 1.RAND SELECT RAND () ---0.302870228294199取0-1之間的隨機小數。 2.小數取整 CEILINT(data)舍掉小數部分并向上取整。FLOOR(data)舍掉小數部分并向下取整。SELECT TOP 3 FWeight, CEILING(FWeight ),FLOOR( FWeight) FROM T_PersonRound(m,d)&#xff1a;四舍…

html div模塊前留空白,html – 3個DIV彼此相鄰,中間填充空白

您好我想問你如何將3 DIV放在一起,而中間一個填補第一和第三DIV之間的空白.我想在第一個NAD第三個DIV中有動態按鈕,我需要中間DIV來填充第一和第三個DIV之間的空間.我會破壞純CSS / HTML(沒有JavaScript)這是我的嘗試&#xff1a;http://jsfiddle.net/4smx3627/#wrapper{height…

mplayer安裝記錄 源碼分析

mplayer源碼下載地址&#xff1a; http://www.mplayerhq.hu/MPlayer/releases/ 下載最新的MPlayer-1.0rc4 #mkdir /usr/local/mplayer #mkdir /usr/local/codecs #cd MPlayer-1.0rc4 #./configure --prefix/usr/local/mplayer --codecsdir/usr/local/ codecs --langua…

python人臉識別代碼百度ai_python百度AI人臉識別API測試

1、注冊賬號 2、創建應用 3、得到AK和SK 4、用AK SK獲取access_token 可用下面的代碼&#xff1a; #!/usr/bin/python3.5 # encoding:utf-8 import requests # client_id 你的AK client_secret 你的SK host https://aip.baidubce.com/oauth/2.0/token?grant_typeclient_crede…

Flask 第三方組件之 SQLAlchemy

一、介紹 SQLAlchemy是一個基于Python實現的ORM框架。該框架建立在 DB API之上&#xff0c;使用關系對象映射進行數據庫操作&#xff0c;簡言之便是&#xff1a;將類和對象轉換成SQL&#xff0c;然后使用數據API執行SQL并獲取執行結果。 安裝&#xff1a;pip3 install sqlalc…

httpservlet獲取請求端IP地址

request.getRemoteAddr(); 轉載于:https://www.cnblogs.com/panxuejun/p/7623850.html

html 中怎樣顯示enum,JavaScript如何枚舉?

JavaScript中對象的屬性分為兩種&#xff1a;數據屬性和訪問器屬性。然后根據具體的上下文環境的不同&#xff0c;又可以將屬性分為&#xff1a;原型屬性和實例屬性。原型屬性是定義在對象的原型(prototype)中的屬性&#xff0c;而實例屬性一方面來自構造的函數中&#xff0c;然…

iperf測試網卡性能

Iperf是一個網絡性能測試工具。可以測試TCP和UDP帶寬質量&#xff0c;可以測量最大TCP帶寬&#xff0c;具有多種參數和UDP特性&#xff0c;可以報告帶寬&#xff0c;延遲抖動和數據包丟失 因為產品上確定要要用的&#xff30;&#xff28;&#xff39;是千&#xff2d;的&a…

acrobat 控件可以發布嗎_短視頻可以同時在多個平臺發布嗎?

我們在做自媒體內容創業中&#xff0c;很多人都在做視頻版塊&#xff0c;那么一個短視頻到底能不能多平臺同時發布呢&#xff1f;那么今天&#xff0c;我來分享給大家&#xff0c;希望能夠幫到你解決困惑。1.作品可以多平臺分發&#xff1a;大家不確定是否能多平臺分發&#xf…

紅河學院計算機科學與技術,2016年紅河學院計算機科學與技術專業最低分是多少?...

類似問題答案2016年廈門理工學院計算機類(含計算機科學與技術、網絡工程、空間信息與專業最低分...學校 地 區 專業 年份 批次 類型 分數 廈門理工學院 福建 計算機類(含計算機科學與技術、網絡工程、空間信息與 2016 一批 理科 491 學校 地 區 專業 年份 批次 類型 分數 廈門理…

Flask 第三方組件之 script

Flask Script擴展提供向Flask插入外部腳本的功能&#xff0c;包括運行一個開發用的服務器&#xff0c;一個定制的Python shell&#xff0c;設置數據庫的腳本&#xff0c;cronjobs&#xff0c;及其他運行在web應用之外的命令行任務&#xff1b;使得腳本和系統分開&#xff1b; …

CentOS四種方法自建yum倉庫

將ISO光盤鏡像作為yum本地倉庫&#xff08;適用于不能聯外網的環境&#xff09;&#xff1a; 1、 禁用所有可用的yum倉庫&#xff0c;為方便演示&#xff0c;直接全部刪除&#xff1a; # cd /etc/yum.repos.d # ls # rm -rf * 2、 創建光盤掛載點&#xff0c;掛載光盤&#x…

python substr_python數據分析-數據對象(一)

Python基本數據類型一般分為&#xff1a;數字、字符串、列表、元組、字典、集合這六種基本數據類型。不可變&#xff08;3 個&#xff09;&#xff1a;Number&#xff08;數字&#xff09;、String&#xff08;字符串&#xff09;、Tuple&#xff08;元組&#xff09;&#xff…

VLC框架分析

功能部份: VLC媒體播放器的核心是libvlc &#xff0c;它提供了界面&#xff0c;應用處理功能&#xff0c;如播放列表管理&#xff0c;音頻和視頻解碼和輸出&#xff0c;線程系統。所有libvlc源文件設在的/src目錄及其子目錄&#xff1a; # config/ &#xff1a;從命令行和配置…

html表格里的超鏈接點不了,Excel如何添加和取消超鏈接 Excel超鏈接打不開是怎么回事...

很多用戶在制作excel表格的時候都會添加一些超鏈接&#xff0c;在制作完成后發布到網頁&#xff0c;閱讀者可以通過超鏈接打開指引的網頁或者文件&#xff0c;超鏈接對制作excel表格的用戶有非常大的幫助&#xff0c;雖然添加超鏈接的步驟非常簡單&#xff0c;不過還是有些exce…

yum 安裝apache php mysql

安裝&#xff1a; yum install -y httpd php 查看版本&#xff1a;、 rpm -qa httpd php httpd-2.2.15-54.el6.centos.x86_64 php-5.3.3-48.el6_8.x86_64 修改apache配置文件&#xff1a; vim /etc/httpd/conf/httpd.conf 在#ServerName www.example.com:80行下添加一行 Server…

Python 散點圖線性擬合_機器學習之利用Python進行簡單線性回歸分析

前言&#xff1a;在利用機器學習方法進行數據分析時經常要了解變量的相關性&#xff0c;有時還需要對變量進行回歸分析。本文首先對人工智能/機器學習/深度學習、相關分析/因果分析/回歸分析等易混淆的概念進行區分&#xff0c;最后結合案例介紹如何利用Python進行簡單線性回歸…

Flask 第三方組件之 Migrate

flask-migrate是flask的一個擴展模塊,主要是擴展數據庫表結構的.類似于Django的python manage.py migrate 官方文檔: http://flask-migrate.readthedocs.io/en/latest/ 安裝 pip install flask-migrate 使用舉例 from flask import Flask from flask_sqlalchemy import SQLA…

html section 布局,section標簽的用法

標簽的用法由于昨晚發了一篇文章http://www.zcool.com.cn/article/ZMzA3MzI.html&#xff0c;有一個網友評論問 的用法。所以現在舉例來說明一下&#xff1a;html5引入了標簽&#xff0c;用于描述文檔的結構&#xff0c;它同標簽的意思一樣。但是在特定環境中&#xff0c;兩者又…