整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Flask博客實戰 - 使用 WTForms 進行表

          Flask博客實戰 - 使用 WTForms 進行表單驗證

          一章節我們通過在html中直接編寫表單的方式進行數據傳遞,并且在視圖中對前端傳遞的數據進行了簡單的認證,但是如果把驗證數據的代碼與邏輯混合在一起,將使得視圖的代碼不夠清晰,并且難以維護,稍加疏忽就會產生驗證漏洞,如果細心的同學其實可以發現,在之前的登錄注冊中我們一直沒有對空表單進行驗證,當然這是我故意為之,但如果在生產環境,這將是一個災難的開始,所以,在編程中無論是前端還是后端都要求要對數據進行驗證,作為后端,更要保持一種永遠不相信前端傳遞數據的態度去做數據校驗。

          本章節我們將使用Flask官方推薦的Flask-WTF擴展來重構我們的登錄注冊表單!

          關于Flask-WTF

          Flask-WTF是Flask 和 WTForms 的簡單集成,包括 CSRF、文件上傳和 reCAPTCHA。

          • 安裝Flask-WTF:
          pip install Flask-WTF

          創建登錄注冊表單類

          app/auth/目錄下新建一個forms.py的文件,所有的表單驗證代碼都放到這個文件當中!

          構建登錄表單

          from flask_wtf import FlaskForm
          from wtforms import StringField, PasswordField
          from wtforms.validators import DataRequired, Length, ValidationError, EqualTo
          from werkzeug.security import check_password_hash
          from .models import User
          
          class LoginForm(FlaskForm):
              # 登錄表單
          
              def qs_username(username):
                  # 對該字段進行在傳遞之前處理
                  u=f'{username}123456'
                  print(u)
                  return username
          
              username=StringField('username', validators=[
                  DataRequired(message="不能為空"), 
                  Length(max=32, message="不符合字數要求!")
                  ], filters=(qs_username,))
              password=PasswordField('password', validators=[
                  DataRequired(message="不能為空"),
                  Length(max=32, message="不符合字數要求!")
                  ])
          
              def validate_username(form, field):
                  user=User.query.filter_by(username=field.data).first()
                  if user is None:
                      error='該用戶不存在!'
                      raise ValidationError(error)
                  elif not check_password_hash(user.password, form.password.data):
                      raise ValidationError('密碼不正確')

          代碼詳解:

          class LoginForm(FlaskForm): 創建了一個登錄表單類,繼承了FlaskForm類

          StringField, PasswordField

          這些都是wtforms內置的字段,負責呈現和數據轉換。

          官方文檔:https://wtforms.readthedocs.io/en/3.0.x/fields/

          他繼承自Filed的基類,其中有一些比較重要的參數我們大概在這里理解一下!

          第一個字符串其實是該類的label參數,字段的標簽,也就是轉換到html中的label!

          validators傳入對該字段的一些驗證器,在提交數據之前對數據進行驗證!

          filters這個參數比較特殊,官方文檔并沒有對其詳細說明,只說是篩選器,其實怎么說就是在額外的方法中對該字段的值提前處理過濾,元組中的每個值都是一個回調函數,函數不需要傳入括號,但這個回調函數默認有一個參數,這個參數就是本身該字段的值,所以在定義該函數時就必須傳入一個參數!例如:我們定義username之前定義的這個方法!

          def qs_username(username):
              # 對該字段進行在傳遞之前處理
              u=f'{username}123456'
              print(u)
              return username

          備注:必須返回處理后的這個參數,否則會觸發DataRequired驗證器,后端獲取不到該表單的值!

          • DataRequired, Length 這是內置的驗證器,第一個是驗證字段是否為空,第二個Length是驗證輸入長度,當然內置的還有很多,這里就不一一列舉,具體我們可參考文檔!

          官方文檔: https://wtforms.readthedocs.io/en/3.0.x/validators/#custom-validators

          自定義驗證用戶名和密碼

          在之前的視圖函數中我們對用戶名和密碼都做了校驗,現在我們需要把驗證的代碼全部移動到表單類中,代碼如下:

          def validate_username(form, field):
              user=User.query.filter_by(username=field.data).first()
              if user is None:
                  error='該用戶不存在!'
                  raise ValidationError(error)
              elif not check_password_hash(user.password, form.password.data):
                  raise ValidationError('密碼不正確')
          • validate_username(form, field)

          這個函數的寫法是固定的validate_{filed},validate_后邊的filed是指你需要驗證的某個字段名,比如我們這個驗證,他主要就是對username字段進行驗證,這個函數中參數的filed就是這個字段,通過field.data就可以獲取到usernam的值。form參數則指代的是整個表單,可以用form.{filed}.data的方式獲取表單類中某個具體字段的值!

          構建注冊表單

          當了解了登錄表單后,我們完全就可以參照登錄表單去實現注冊表單,代碼如下:

          路徑:app/auth/forms.py

          class RegisterForm(FlaskForm):
              # 注冊表單
              username=StringField('username', validators=[
                  DataRequired(message="不能為空"), 
                  Length(min=2, max=32, message="超過限制字數!")
                  ])
              password=PasswordField('password', validators=[
                  DataRequired(message="不能為空"),
                  Length(min=2, max=32, message="超過限制字數!"),
                  EqualTo('password1', message='兩次密碼輸入不一致!')
                  ])
              password1=PasswordField('password1')
          
              def validate_username(form, field):
                  user=User.query.filter_by(username=field.data).first()
                  if user is not None:
                      error='該用戶名稱已存在!'
                      raise ValidationError(error)

          這里唯一需要注意的是兩次密碼是否輸入一致,我們用了一個內置的驗證器EqualTo,使用方式可完全參照代碼,他會自動校驗password和password1輸入的值是否一致!

          重構登錄和注冊視圖

          • 路徑:app/auth/views/auth.py
          from ..forms import LoginForm, RegisterForm
          
          @bp.route('/login', methods=['GET', 'POST'])
          def login():
              # 登錄視圖
              # form=LoginForm(meta={'csrf': False}) # 禁用csrf
              form=LoginForm()
              if form.validate_on_submit():
                  user=auth.User.query.filter_by(username=form.username.data).first()
                  session.clear()
                  session['user_id']=user.id
                  return redirect(url_for('index'))
              return render_template('login.html', form=form)
          
          
          @bp.route('/register', methods=['GET', 'POST'])
          def register():
              # 注冊視圖
              form=RegisterForm()
              if form.validate_on_submit():
                  user=auth.User(username=form.username.data, password=generate_password_hash(form.password.data))
                  db.session.add(user)
                  db.session.commit()
                  session.clear()
                  session['user_id']=user.id
                  return redirect(url_for('index'))
              return render_template('register.html', form=form)

          1、首先從forms.py中引入了我們定義的登錄(LoginForm)和注冊(RegisterForm)表單類!

          2、form=RegisterForm() 實例化表單類

          3、if form.validate_on_submit(): 驗證前端傳遞的數據是否有效,并且會自動判斷是POST請求還是GET請求!

          4、 數據驗證通過則進入之后的邏輯,未驗證通過則返回我們在表單類中傳入的驗證提示!

          模板中調用驗證信息

          我們以調用username字段的驗證提示為例,在模板中加入這段代碼即可獲得錯誤提示!

          <!-- 表單驗證 -->
          {% if form.username.errors %}
          <b-message type="is-danger">
              <ul class="errors">
                  {% for error in form.username.errors %}
                      <li>{{ error }}</li>
                  {% endfor %}
              </ul>
          </b-message>
          {% endif %}

          重構登錄注冊html模板

          路徑:app/auth/templates/login.html 以登陸表單為例,代碼如下:

          {% block auth_form %}
              <form action="" method="post" style="margin-top: 40%;" class="box">
                  <div class=" has-text-centered mb-3">
                    <p class=" subtitle">登錄</p>
                    <h1 class="title">FlaskBlog</h1>
                  </div>
                  {{ form.csrf_token }}
                  <!-- 消息閃現 -->
                  {% with messages=get_flashed_messages() %}
                  <b-message type="is-danger">
                    {% if messages %}
                    <ul class=flashes>
                      {% for message in messages %}
                      <li>{{ message }}</li>
                      {% endfor %}
                    </ul>
                    {% endif %}
                  </b-message>
                  {% endwith %}
          
                  <!-- 表單驗證 -->
                  {% if form.username.errors %}
                  <b-message type="is-danger">
                    <ul class="errors">
                      {% for error in form.username.errors %}
                      <li>{{ error }}</li>
                      {% endfor %}
                    </ul>
                  </b-message>
                  {% endif %}
          
                  <div class="field">
                    <p class="control has-icons-left has-icons-right">
                      {{ form.username(class='input', placeholder='Username') }}
                      <span class="icon is-small is-left">
                        <i class="fas fa-envelope"></i>
                      </span>
                      <span class="icon is-small is-right">
                        <i class="fas fa-check"></i>
                      </span>
                    </p>
                  </div>
                  <div class="field">
                    <p class="control has-icons-left">
                      {{ form.password(class='input', placeholder='Password') }}
                      <span class="icon is-small is-left">
                        <i class="fas fa-lock"></i>
                      </span>
                    </p>
                  </div>
                  <div class="field">
                    <p class="control">
                      <input class="button is-success is-fullwidth" type="submit" value="Login">
                    </p>
                  </div>
                </form>
              {% endblock auth_form %}

          {{ form.csrf_token }} 隱式的創建一個csrftoken的表單

          {{ form.username(class='input', placeholder='Username') }} 這樣就可以直接獲得一個表單html并自動渲染,向該表單增加書香的方式就是像代碼中這樣傳入參數和值即可,當然也可以提前在表單類中定義!

          剩下的注冊表單,就當是給大家留作的一個作業,大家自行去參照登錄表單完善重構一下,加油哦!我相信你可以!

          到這里我們的表單驗證就大概了解了,之后的章節就是基本的增刪改查以及表單驗證,都是基于我們這些章節學習的知識點,所以之后的章節就不會過多的去講解每行代碼的意思,重心放在邏輯的展示上,如果基礎較差的同學,到這里,可以去反復的把前邊所有章節的內容去練習,寫代碼其實就是寫的多了就會了,也就理解了,練習 練習 再練習!

          者:俊欣

          來源:關于數據分析與可視化

          今天小編帶領大家用Python自制一個自動生成探索性數據分析報告這樣的一個工具,大家只需要在瀏覽器中輸入url便可以輕松的訪問,如下所示

          第一步

          首先我們導入所要用到的模塊,設置網頁的標題、工具欄以及logo的導入,代碼如下

          from st_aggrid import AgGrid
          import streamlit as st
          import pandas as pd
          import pandas_profiling
          from streamlit_pandas_profiling import st_profile_report
          from pandas_profiling import ProfileReport
          from  PIL import Image
          
          st.set_page_config(layout='wide') #Choose wide mode as the default setting
          
          #Add a logo (optional) in the sidebar
          logo=Image.open(r'wechat_logo.jpg')
          st.sidebar.image(logo,  width=120)
          
          #Add the expander to provide some information about the app
          with st.sidebar.expander("關于這個項目"):
               st.write("""
                  該項目是將streamlit和pandas_profiling相結合,在您上傳數據集之后自動生成相關的數據分析報告,當然該項目提供了兩種模式 全量分析還是部分少量分析,這里推薦用部分少量分析,因為計算量更少,所需要的時間更短,效率更高
               """)
          
          #Add an app title. Use css to style the title
          st.markdown(""" <style> .font {                                          
              font-size:30px ; font-family: 'Cooper Black'; color: #FF9633;} 
              </style> """, unsafe_allow_html=True)
          st.markdown('<p class="font">請上傳您的數據集,該應用會自動生成相關的數據分析報告</p>', unsafe_allow_html=True)
          

          output

          上傳文件以及變量的篩選

          緊接的是我們需要上傳csv文件,代碼如下

          uploaded_file=st.file_uploader("請上傳您的csv文件: ", type=['csv'])
          

          我們可以選擇針對數據集當中所有的特征進行一個統計分析,或者只是針對部分的變量來一個數據分析,代碼如下

          if uploaded_file is not None:
               df=pd.read_csv(uploaded_file)
               option1=st.sidebar.radio(
                    '您希望您的數據分析報告中包含哪些變量呢',
                    ('所有變量', '部分變量'))
          
               if option1=='所有變量':
                    df=df
          
               elif option1=='部分變量':
                    var_list=list(df.columns)
          

          要是用戶勾選的是部分變量,只是針對部分變量來進行一個分析的話,就會彈出來一個多選框來供用戶選擇,代碼如下

          var_list=list(df.columns)
          option3=st.sidebar.multiselect(
               '篩選出您希望在數據分析報告中包含的變量',
               var_list)
          df=df[option3]
          

          用戶可以挑選到底是“簡單分析”或者是“完整分析”,要是勾選的是“完整分析”的話,會跳出相應的提示,提示“完整分析”由于涉及到更加復雜的計算操作,耗時更加地長,要是遇到大型的數據集,還會有計算失敗的情況出現

           option2=st.sidebar.selectbox(
                '篩選模式,完整分析還是簡單分析',
                ('簡單分析', '完整分析'))
          
           if option2=='完整分析':
                mode='complete'
                st.sidebar.warning(
                     '完整分析由于涉及到更加復雜的計算操作,耗時更加地長,要是遇到大型的數據集,還會有計算失敗的情況出現,這里推薦使用簡單分析')
           elif option2=='簡單分析':
                mode='minimal'
                grid_response=AgGrid(
                     df,
                     editable=True,
                     height=300,
                     width='100%',
                )
          
                updated=grid_response['data']
                df1=pd.DataFrame(updated)
          

          當用戶點擊“生成報告”的時候就會自動生成一份完整的數據分析報告了,代碼如下

          if st.button('生成報告'):
                  if mode=='complete':
                      profile=ProfileReport(df,
                          title="User uploaded table",
                          progress_bar=True,
                          dataset={
                              "簡介": '歡迎關注公眾號:關于數據分析與可視化',
                              "作者": '俊欣',
                              "時間": '2022.05'
                          })
                      st_profile_report(profile)
                  elif mode=='minimal':
                      profile=ProfileReport(df1,
                          minimal=True,
                          title="User uploaded table",
                          progress_bar=True,
                          dataset={
                              "簡介": '歡迎關注公眾號:關于數據分析與可視化',
                              "作者": '俊欣',
                              "時間": '2022.05'
                          })
                      st_profile_report(profile)
          

          最后出來的結果如下,這里再來顯示一遍

          取網頁圖片的基本流程是:

          1 使用urllib.request模板請求返回網頁文本;

          2 從網頁文本中使用正則表達式篩選出img src地址(返回一個全部src的列表);

          3 圖片文件逐一檢索或復制;

          代碼:

          運行效果:

          附代碼1:

          import re

          import urllib.request

          import os

          #1 抓取網頁

          #url='http://www.kgc.cn/list'

          url='http://www.ttpaihang.com/vote/rank.php?voteid=1410&page=2'

          req=urllib.request.urlopen(url)

          buf=req.read()

          req.close()

          #2 獲取圖片地址

          i=url.find("/",9) # 本句及下面三句截取url的前半截

          url2=url

          if i > 0 :

          ....url2=url[:i]

          #buf=buf.decode('UTF-8')

          buf=buf.decode('gb2312')

          #listurl=re.findall(r'http:.[^"]+\.jpg',buf)

          listurl=re.findall(r'img src=.[^"]+\.jpg',buf)

          for i in range(len(listurl)):.... # 把字符img src="去掉

          ....listurl[i]=listurl[i].replace('img src="',"")

          ....if not re.match("http",listurl[i]):

          ........listurl[i]=url2 + listurl[i]

          ....print(listurl[i])

          #3 抓取圖片并保存到本地

          i=0

          fpath="D:\pic2\"

          if not os.path.isdir(fpath):

          ....os.mkdir(fpath)

          for url in listurl:

          ....f=open(fpath + str(i)+'.jpg','wb')

          ....req=urllib.request.urlopen(url)

          ....buf=req.read()

          ....f.write(buf)

          ....f.close()

          ....i+=1

          ........

          附代碼2(寫成函數的形式)

          import re .... .... .... .... # 正則表達式

          import urllib.request .... .... # 從服務器請求返回資源

          import os .... .... .... .... # 文件和目錄操作

          import socket .... .... .... .... # 套接字操作

          #socket.setdefaulttimeout(20)....................# 設置socket層的超時時間為20秒

          def gethtml(url): #1 抓取網頁html內容

          ....with urllib.request.urlopen(url) as req:

          ........buf=req.read()

          ........return buf

          def getImg(buf,codec,fpath): #2 從html篩選圖片地址到list

          ....i=url.find("/",9)............................ # 本句及下面三句截取url的前半截

          ....url2=url

          ....if i > 0 :

          ........url2=url[:i]

          ....buf=buf.decode(codec)

          ....

          ....reg=r'img src="(.+?\.jpg)"'....#正則表達式,得到圖片地址

          ....#listurl=re.findall(r'http:.[^"]+\.jpg',buf)

          ....#listurl=re.findall(r'img src=.[^"]+\.jpg',buf)

          ....listurl=re.findall(reg,buf)

          ....print("準備下載圖片數量:",len(listurl))

          ....for i in range(len(listurl)):................

          ........#listurl[i]=listurl[i].replace('img src="',"") # 把字符img src="去掉

          ........if not re.match("http",listurl[i]):

          ............listurl[i]=url2 + listurl[i]

          ........print(listurl[i])

          ............#3 抓取圖片并保存到本地

          ....i=0

          ....

          ....if not os.path.isdir(fpath):

          ........os.mkdir(fpath)

          ....'''

          ....for imgurl in listurl:

          ........urllib.request.urlretrieve(imgurl,fpath + str(i)+'.jpg')

          ........i+=1

          ....'''#下面的操作方式要快一點

          ....for imgurl in listurl:

          ........f=open(fpath + str(i)+'.jpg','wb') # 新建空白圖片文件

          ........req=urllib.request.urlopen(imgurl) # 獲取網頁圖片文件

          ........buf=req.read().... .... .... # 讀取網站上圖片文件內容

          ........f.write(buf).... .... .... # 將網站上圖片內容寫入新建的圖片文件

          ........f.close()

          ........i+=1

          # 四處內容需要確認:1 網頁url; .... ....2 網頁編碼UTF-8或gb2312;

          #................ 3 圖片擴展名jpg或png(兩處); 4 保存的文件夾

          #url='http://www.kgc.cn/list'

          url='http://www.ttpaihang.com/vote/rank.php?voteid=1410&page=3'

          buf=gethtml(url)

          #codec='UTF-8'

          codec='gb2312'

          fpath="D:\pic4\"

          print(getImg(buf,codec,fpath))

          -End-


          主站蜘蛛池模板: 日本一区二区三区免费高清| 亚洲一区电影在线观看| 视频一区二区精品的福利| 中文字幕日韩欧美一区二区三区| 在线视频一区二区三区四区| 日本精品视频一区二区| 精品一区二区三区影院在线午夜| 国产精品无码一区二区三级| 夜夜添无码一区二区三区| 国产一国产一区秋霞在线观看| 一区二区三区日韩| 中文字幕无线码一区| 一级特黄性色生活片一区二区| 国产福利一区视频| 亚洲乱色熟女一区二区三区丝袜 | 果冻传媒董小宛一区二区| 狠狠综合久久AV一区二区三区| 久久国产精品亚洲一区二区| 国产美女一区二区三区| 国产成人精品一区二三区| 国产一区二区免费| 精品人妻无码一区二区色欲产成人 | 久久国产精品无码一区二区三区| 国产精品亚洲专一区二区三区| 亚洲一区日韩高清中文字幕亚洲| 国产丝袜美女一区二区三区| 韩国理伦片一区二区三区在线播放| 波多野结衣一区二区免费视频| 亚洲一区二区女搞男| 一区二区三区精密机械| 偷拍精品视频一区二区三区| 久久久久久人妻一区精品| 国产一区二区三区在线观看影院| 国产亚洲一区二区手机在线观看| 亚洲国产精品无码第一区二区三区| 国产精华液一区二区区别大吗| 免费看无码自慰一区二区| 一区二区视频在线观看| 一区二区福利视频| 理论亚洲区美一区二区三区 | 国产精品va无码一区二区|