類比Django的Form組件
Form組件的主要應用是幫助咱們自動生成HTML代碼和作一些表單數據的驗證css
flask的wtforms用法跟Form組件大同小異
參考文章:http://www.javashuo.com/article/p-daydujxg-g.htmlhtml
下載安裝
pip install wtforms前端
1. wtforms支持的字段和驗證函數
原文:https://blog.csdn.net/wuqing942274053/article/details/72510920html5
WTForms支持的HTML標準字段正則表達式
字段類型 | 說明 |
---|---|
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 | 一組指定類型的字段 |
WTForms驗證函數數據庫
驗證函數 | 說明 |
---|---|
驗證電子郵件地址 | |
EqualTo | 比較兩個字段的值,經常使用於要求輸入兩次密碼進行確認的狀況 |
IPAddress | 驗證IPv4網絡地址 |
Length | 驗證輸入字符串的長度 |
NumberRange | 驗證輸入的值在數字範圍內 |
Optional | 無輸入值時跳過其餘驗證函數 |
Required | 確保字段中有數據 |
Regexp | 使用正則表達式驗證輸入值 |
URL | 驗證URL |
AnyOf | 確保輸入值在可選值列表中 |
NoneOf | 確保輸入值不在可選列表中 |
2. wtforms類的屬性和方法
屬性:
data
包含每一個字段的數據的字典npm
errors
包含每一個字段的錯誤列表的DECT。若是沒有驗證表單,或者沒有錯誤,則爲空。flask
meta
這是一個包含各類配置選項以及自定義表單行爲的能力的對象。有關可使用類元選項自定義的內容的更多信息,請參見類元文檔。bootstrap
方法:
validate():經過在每一個字段上調用Value來驗證表單,將任何額外的Form.Value_<field name>驗證器傳遞給字段驗證器。
populate_obj(obj):使用表單字段中的數據填充傳遞的obj的屬性。
__iter__():按建立順序迭表明單字段。
__contains__(name):若是指定的字段是此表單的成員,則返回True。後端
例如:
form_obj = wtform(request.form) if form_obj.validate(): # 包含每一個字段的數據的字典 print(form_obj.data) # 這是一個包含各類配置選項以及自定義表單行爲的能力的對象 print(form_obj.meta) return "註冊成功" # 包含每一個字段的錯誤列表的DECT。若是沒有驗證表單,或者沒有錯誤,則爲空。 print(form_obj.errors)
字段的基類:
label | 字段的標籤 |
validators | 驗證器 -驗證的序列時要調用驗證被調用 |
default | 若是未提供表單或對象輸入,則分配給字段的默認值 |
widget | 若是提供,則覆蓋用於呈現字段的窗口小部件 |
render_kw | 設置字段的額外參數,提供一個字典 |
filters | 按進程在輸入數據上運行的一系列過濾器。 |
description | 字段的描述,一般用於幫助文本 |
id | 用於字段的id。表單設置了合理的默認值,您不須要手動設置。 |
_form | 包含此字段的表單。在施工期間,它由表格自己傳遞。你永遠不該該本身傳遞這個值。 |
_name | 此字段的名稱,由封閉表單在構造期間傳遞。你永遠不該該本身傳遞這個值 |
_prefix | 前綴爲此字段的表單名稱的前綴,在構造期間由封閉表單傳遞。 |
_translations | 提供消息翻譯的翻譯對象。一般在施工期間經過封閉的形式經過。 |
_meta | 若是提供,這是表單中的'meta'實例。你一般不會本身經過 |
1. MyForms.py 定義Form表單的類
from wtforms import Form, widgets from wtforms.fields import simple, core, html5 # 使用wtforms的類必需要繼承它的Form類 # simple:普通字段 core:核心字段 html5:H5新增的字段 class RegisterForm(Form): username = simple.StringField( label='用戶名', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, # widget插件,能夠把這個字段設置成其餘type類型 widget=widgets.TextArea() ) pwd = simple.PasswordField( label='密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'} ) re_pwd = simple.PasswordField( label='確認密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, )
2. 視圖
from flask import Blueprint, render_template from FlaskPlug.utils.MyForms import RegisterForm userBlue = Blueprint("userBlue", __name__) @userBlue.route('/register') def register(): # 實例化form form_obj = RegisterForm() return render_template('register.html', form_obj=form_obj)
3. HTML代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4"> <h2 style="margin-top: 50px;margin-bottom: 30px;text-align: center">歡迎註冊</h2> <form action="" method="POST" novalidate class="form-horizontal"> {% for field in form_obj %} <div class="form-group"> {{ field.label }} {{ field }} </div> {% endfor %} <button type="submit" class="btn btn-success">提交</button> </form> </div> </div> </div> </body> </html>
步驟
1. 在Form類中增長驗證信息
2. 在視圖中作數據的校驗 而且頁面展現錯誤信息
1. MyForms.py 定義Form表單的類
from wtforms import Form, widgets, validators from wtforms.fields import simple, core, html5 class RegisterForm(Form): username = simple.StringField( label='用戶名', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, # 能夠定義多個校驗規則 validators=[ # DataRequired字段必填 validators.DataRequired(message='用戶名不能爲空'), # length字段的長度限制 # message:用戶填寫錯誤時的錯誤信息 validators.length(min=2, max=8, message='長度必須在2-8之間') ], # widget=widgets.TextArea() ) pwd = simple.PasswordField( label='密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間') ], ) re_pwd = simple.PasswordField( label='確認密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間'), # EqualTo:校驗兩個字段的值是否相等 validators.EqualTo('pwd', message='兩次密碼不一致') ], ) phone = simple.StringField( label='手機號碼', validators=[ validators.Regexp(regex="^1[3-9][0-9]{9}$",message='手機格式不正確') ] )
2. 視圖
from flask import Blueprint, render_template, request from FlaskPlug.utils.MyForms import RegisterForm from FlaskPlug.models import User userBlue = Blueprint("userBlue", __name__) @userBlue.route('/register', methods=['GET', 'POST']) def register(): # 實例化form form_obj = RegisterForm() if request.method == "POST": # 把用戶提交上來的數據放入Form表單中實例化 form_obj = RegisterForm(request.form) # validate方法會去校驗用戶提交上來的數據 if form_obj.validate(): # 驗證經過能夠寫入數據庫,這裏演示,不寫入 # 驗證經過的數據都保存在data這個大字典裏面 # username = form_obj.data.get('username') # password = form_obj.data.get('pwd') # user_obj = User(username=username, password=password) # db.session.add(user_obj) # db.session.commit() # db.session.close() return "註冊成功" return render_template('register.html', form_obj=form_obj)
3. HTML代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4"> <h2 style="margin-top: 50px;margin-bottom: 30px;text-align: center">歡迎註冊</h2> <form action="" method="POST" novalidate class="form-horizontal"> {% for field in form_obj %} <div class="form-group"> {{ field.label }} {{ field }} <span style="color: red">{{ field.errors[0] }}</span> </div> {% endfor %} <button type="submit" class="btn btn-success">提交</button> </form> </div> </div> </div> </body> </html>
from wtforms import Form, validators, ValidationError from wtforms.fields import simple def check_username(form, field): if len(field.data) < 2: raise ValidationError('錯了,嘿嘿') class TestForm(Form): username = simple.StringField( label='用戶名', validators=[check_username, ] )
局部鉤子函數: validate_字段名,接收兩個參數(form, field),後端調用validate()校驗函數的時候觸發 form: wtforms類的實例 field: 字段對象,能夠經過field.data獲取前端傳過來的該字段的數據,不是字典 全局鉤子函數:validate, 接收self參數,self.data表明前端表單傳過來的全部數據,是一個字典 from wtforms import Form, validators, ValidationError from wtforms.fields import simple class TestForm(Form): username = simple.StringField( label='用戶名', ) password = simple.PasswordField( label='密碼', validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間') ] ) # 局部鉤子函數 def validate_username(form, field): print(field.data) # 張三 if len(field.data) < 2: raise ValidationError('用戶名至少兩位字符') # 全局鉤子函數 def validate(self): print(self.data) # {'username': '張三', 'password': '12345678'} for key, value in self.data.items(): print(value)
from wtforms import Form, widgets, validators from wtforms.fields import simple, core, html5 class RegisterForm(Form): username = simple.StringField( label='用戶名', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, # 能夠定義多個校驗規則 validators=[ # DataRequired字段必填 validators.DataRequired(message='用戶名不能爲空'), # length字段的長度限制 # message:用戶填寫錯誤時的錯誤信息 validators.length(min=2, max=8, message='長度必須在2-8之間') ], # widget=widgets.TextArea() ) pwd = simple.PasswordField( label='密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間') ], ) re_pwd = simple.PasswordField( label='確認密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間'), # EqualTo:校驗兩個字段的值是否相等 validators.EqualTo('pwd', message='兩次密碼不一致') ], ) phone = simple.StringField( label='手機號碼', validators=[ validators.Regexp(regex="^1[3-9][0-9]{9}$",message='手機格式不正確') ] ) # H5新增的標籤email email = html5.EmailField( label='郵箱', validators=[ validators.DataRequired(message='郵箱不能爲空.'), ], widget=widgets.TextInput(input_type='email'), ) # 核心字段core,單選框 gender = core.RadioField( label='性別', choices=((1, '男'), (2, '女')), # 前端傳過來的數據是字符串類型,coerce能夠把穿過來的數據轉換類型 # 由於數據庫存的1是int類型,前端選擇"男",傳過來的是字符串1 coerce=int, default=1 ) # 單選下拉菜單 city = core.SelectField( label='城市', choices=(('sz', '深圳'), ('gz', '廣州'), ) ) # 多選下拉菜單 hobby = core.SelectMultipleField( label='愛好', choices=( (1, '美女'), (2, 'xiong'), ), ) 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 = ORM操做 # 這裏演示一下更改 self.favor.choices = ((1, '籃球'), (2, '足球'), (3, '羽毛球'))
1.Form類
from flask import request from wtforms import Form from wtforms.csrf.core import CSRF from wtforms.fields import simple from wtforms import validators from wtforms import widgets from hashlib import md5 # 自定義CSRF類,重寫CSRF的三個方法 class MyCSRF(CSRF): """ Generate a CSRF token based on the user's IP. I am probably not very secure, so don't use me. """ def setup_form(self, form): # 獲取class Meta裏設置的一些值 self.csrf_context = form.meta.csrf_context() self.csrf_secret = form.meta.csrf_secret # 調用父類的setup_form方法 return super(MyCSRF, self).setup_form(form) def generate_csrf_token(self, csrf_token): # 使用md5加密,生成惟一token gid = self.csrf_secret + self.csrf_context token = md5(gid.encode('utf-8')).hexdigest() return token def validate_csrf_token(self, form, field): # 校驗token print(field.data, field.current_token) if field.data != field.current_token: raise ValueError('Invalid CSRF') class RegisterForm(Form): username = simple.StringField( label='用戶名', render_kw={'class': 'form-control'}, widget=widgets.TextInput(), validators=[ validators.DataRequired(message='用戶名不能爲空'), validators.length(min=2, max=8, message='長度必須在2-8之間') ] ) password = simple.PasswordField( label='密碼', render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間') ] ) re_pwd = simple.PasswordField( label='確認密碼', # 給這個字段添加樣式 render_kw={'class': 'form-control'}, validators=[ validators.DataRequired(message='密碼不能爲空'), validators.length(min=8, max=16, message='長度必須在8-16之間'), validators.EqualTo('password', message='兩次密碼不一致') ] ) phone = simple.StringField( label='手機號碼', validators=[ validators.Regexp(regex="^1[3-9][0-9]{9}$", message='手機格式不正確') ] ) class Meta: # -- CSRF # 是否自動生成CSRF標籤 csrf = True # 生成CSRF標籤name csrf_field_name = 'csrf_token' # 自動生成標籤的值,加密用的csrf_secret csrf_secret = '一庫' # 自動生成標籤的值,加密用的csrf_context csrf_context = lambda x: request.url # 生成和比較csrf標籤 csrf_class = MyCSRF # -- i18n # 是否支持本地化 # locales = False locales = ('zh', 'en') # 是否對本地化進行緩存 cache_translations = True # 保存本地化緩存信息的字段 translations_cache = {}
2.models
class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(32), nullable=False) password = db.Column(db.String(32), nullable=False) phone = db.Column(db.String(32))
3.視圖
from flask import request, views, session, render_template from flask_demo.home.models.home_model import * from flask_demo import db from flask_demo.home.forms.home_forms import RegisterForm class RegisterView(views.MethodView): def get(self): form_obj = RegisterForm() return render_template('register.html', form_obj=form_obj) def post(self): form_obj = RegisterForm(request.form) if form_obj.validate(): user_dict = {} user_dict['username'] = form_obj.data.get('username') user_dict['password'] = form_obj.data.get('password') user_dict['phone'] = form_obj.data.get('phone') user_obj = User(**user_dict) db.session.add(user_obj) db.session.commit() db.session.close() return "註冊成功" return render_template('register.html', form_obj=form_obj)
4.HTML代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4"> <h2 style="margin-top: 50px;margin-bottom: 30px;text-align: center">歡迎註冊</h2> <form action="" method="POST" novalidate class="form-horizontal"> {{ form_obj.csrf_token }} <div class="form-group"> {{ form_obj.username.label }} {{ form_obj.username }} <span style="color: red">{{ form_obj.username.errors[0] }}</span> </div> <div class="form-group"> {{ form_obj.password.label }} {{ form_obj.password }} <span style="color: red">{{ form_obj.password.errors[0] }}</span> </div> <div class="form-group"> {{ form_obj.re_pwd.label }} {{ form_obj.re_pwd }} <span style="color: red">{{ form_obj.re_pwd.errors[0] }}</span> </div> <div class="form-group"> {{ form_obj.phone.label }} {{ form_obj.phone }} <span style="color: red">{{ form_obj.phone.errors[0] }}</span> </div> <button type="submit" class="btn btn-success">提交</button> </form> </div> </div> </div> </body> </html>
""" 文件上傳完後,進行代碼的統計 app.config.root_path: 項目的根路徑 os.walk: 遍歷你給的路徑下的全部文件(會遞歸遍歷) 每次循環的根文件夾的路徑,文件夾的名字組成的列表,和文件組成的列表 dirpath, dirnames, filenames zipfile: 壓縮解壓文件的模塊 shutil: 也是壓縮解壓文件的模塊,還能移動啥的 """ from flask import Blueprint, request, render_template from flask import current_app as app import shutil from uploadCode.models import CodeRecord from uploadCode import db import os import time uploadBlue = Blueprint('uploadBlue', __name__) # zip包上傳 @uploadBlue.route('/upload', methods=['GET', 'POST']) def upload(): if request.method == "GET": return render_template("upload.html", error="") # 先獲取前端傳過來的文件 file = request.files.get("zip_file") # 判斷是不是zip包 zip_file_type = file.filename.rsplit(".", 1) if zip_file_type[-1] != "zip": return render_template("upload.html", error="文件必須是zip包") # 解壓路徑 upload_path = os.path.join(app.config.root_path, "files", zip_file_type[0]+str(time.time())) print(upload_path) # 解壓前端傳過來的文件file到upload_path這個路徑 shutil._unpack_zipfile(file, upload_path) # 遍歷保存的文件夾獲得全部.py文件 file_list = [] for (dirpath, dirnames, filenames) in os.walk(upload_path): for filename in filenames: file_type = filename.rsplit(".", 1) if file_type[-1] != "py": continue file_path = os.path.join(dirpath, filename) file_list.append(file_path) # 打開每一個文件讀取行數 sum_num = 0 for path in file_list: with open(path, mode="rb") as f: for line in f: if line.strip().startswith(b"#"): continue sum_num += 1 # 獲得總行數去保存數據庫 return str(sum_num)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> 請上傳你的代碼: <input type="file" name="zip_file"> <button type="submit">提交</button> {{error}} </form> </body> </html>
參考文檔Echarts:http://echarts.baidu.com/
1. 下載它須要的依賴包
2. 點擊某個Demo-->Download
3. 參考着Demo去實現你的需求
從後端傳數據到前端的時候,由於展現的柱狀圖須要後端的數據,
而若是把數據直接在JS中使用,會出現一些問題,
所以,咱們可使用一個標籤,給這個標籤設置屬性從而獲取從後端傳過來的數據,
而後在JS中獲取這個標籤的屬性值,就能夠拿到後端的數據了,
可是從屬性獲取的數據已經被轉化成字符串了,咱們這時可使用eval()方法,
把數據類型從新轉換回來。
from flask import Blueprint, request, render_template from uploadCode.models import CodeRecord from uploadCode import db # 柱狀圖 @uploadBlue.route("/") def index(): # 展現用戶提交代碼柱狀圖 queryset = db.session.query(CodeRecord).all() date_list = [] num_list = [] for obj in queryset: date_list.append(str(obj.upload_date)) num_list.append(obj.code_nums) return render_template("index.html", date_list=date_list, num_list=num_list)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/echarts.common.min.js"></script> </head> <body> <div id="container" style="height: 400px"></div> <!--用一個標籤獲取從後端傳過來的數據--> <div id="info" date_list="{{date_list}}" num_list="{{num_list}}"></div> <script> var dom = document.getElementById("container"); var myChart = echarts.init(dom); var app = {}; let infoEle = document.getElementById("info"); let date_list = infoEle.getAttribute("date_list"); let num_list = infoEle.getAttribute("num_list"); option = null; app.title = '座標軸刻度與標籤對齊'; option = { color: ['#3398DB'], tooltip : { trigger: 'axis', axisPointer : { // 座標軸指示器,座標軸觸發有效 type : 'shadow' // 默認爲直線,可選爲:'line' | 'shadow' } }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis : [ { type : 'category', data : eval(date_list), axisTick: { alignWithLabel: true } } ], yAxis : [ { type : 'value' } ], series : [ { name:'直接訪問', type:'bar', barWidth: '60%', data: eval(num_list) // 從新計算一下這個字符串,轉成本來的數據類型 } ] }; ; if (option && typeof option === "object") { myChart.setOption(option, true); } </script> </body> </html>