python Web框架之Flask

python Web框架

我們討論一下Flask和Django

  1. Flask框架一個輕量級的框架,是一個依賴Jinjia2模板引擎和WSGI的微型框架

  2. 安裝Flask

    sudo pip3 install flask

  3. 框架模型,分爲兩種

    1. MTV

      M:Models,模型層。主要通過ORM建立數據庫

      T:Templates,模板層。主要處理前端頁面的顯示,如:html

      V:Views,視圖層。主要根據具體的請求處理具體的業務邏輯

    2. MVC

      M:Models,模型層。主要通過ORM建立數據庫

      V:Views,視圖層,主要處理前端頁面的顯示

      C:Controller , 控制器 ,處理與用戶交互的內容(請求和響應),主要是後臺業務邏輯

Flask框架使用方法如下:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy 
import pymysql

# SQLAlchemy本身無法直接連接數據庫,需要依賴第三方包,pymysql
pymysql.install_as_MySQLdb()


# 創建一個app應用
app = Flask(__name__)

# 爲app指定數據庫的配置文件
app.config['SQLALCHEMY_DATABASE_URI']='mysql://root:123456@localhost:3306/Person'
# 連接數據庫,返回一個操作數據庫的db對象
db = SQLAlchemy(app)

# 創建表結構
class Users(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(80),nullable=False,unique=True)
    age = db.Column(db.Integer)
    email = db.Column(db.String(120),unique=True)
    
    def __init__(self,username,age,email):
        self.username = username
        self.age = age
        self.email = email
	
    # 定義返回的類型
    def __repr__(self):
        return "<User:%r>" % self.username
    
# 將創建好的實體類映射回數據庫
db.create_all()

# 創建一個視圖函數
# 建立請求路徑和視圖函數的映射關係
@app.route('/01-add')
def add_views():
    #創建Users對象,並插入到數據庫中
    users = Users('王老師',35,'[email protected]')
    db.session.add(users)
    db.session.commit()
    return "Add OK"

# 啓動Falsk應用程序
if __name__ == "__main__":
    app.run(debug=True,host='0.0.0.0')

上面就是Falsk的基本用法,下面我們講一下Flask框架自身的特性

路由route

定義:處理URL和函數之間關係的程序。

簡單的說:路由就是將URL綁定到一個函數上面,這樣瀏覽器客戶端向web服務器發送一個URL請求後,服務器中的路由收到這個URL後,能立馬找到對應的函數進行處理。

1.定義路由的方式

# 1.基本路由
@app.route('/')
def index():
    pass
 
# 2.帶參數的路由
@app.route('/<username>')
def show_user(username):
    pass

# 3.帶參數類型的路由
@app.route('/post/<int:post_id>')
def show_post(post_id):
	pass

**同一視圖函數可以定義多個路由

現在來講一下@app.route(’/<username>’)如何實現的,模擬實現的

class Flask_Simulate():
  	def __init__(self):
    	self.routes = []
 
  	def build_route_pattern(route):
    	route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route)
    	return re.compile("^{}$".format(route_regex))
 
  	def route(self, route_str):
    	def decorator(f):
      		route_pattern = self.build_route_pattern(route_str)
      		self.routes.append((route_pattern, f)
      		return f
    	return decorator
 
  	def get_route_match(self, path):
    	for route_pattern, view_function in self.routes:
      		m = route_pattern.match(path)
      		if m:
        		return m.groupdict(), view_function
    	return None
 
  	def serve(self, path):
    	route_match = self.get_route_match(path)
    	if route_match:
      		kwargs, view_function = route_match
      		return view_function(**kwargs)
    	else:
      		raise ValueError('Route "{}"" has not been registered'.format(path))

模板 Templates

  1. 變量

    能夠作爲變量的數據類型

    字符串,整數,小數,列表,元組,字典,字典

    在模板中用 {{變量}}表示

  2. 過濾器

    1. 什麼是過濾器

    2. 過濾器是允許在變量輸出之前按一定的規則改變變量的值

    3. 語法

      {{變量|過濾器}}

      jinjia2模板中常見的過濾器

      過濾器名 說明

      capitalize 首字符變大寫,其他字符變小寫

      lower 將值轉換爲小寫字符

      upper 將值轉換爲大寫字符

      title 將值中的每個單詞的首字符變大寫

      trim 去掉值兩端的空格

    4. <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
          <h1>過濾器示例:</h1>
          <h2>
              <p>capitalize</p>
              <p>
                  {{title|capitalize}}
              </p>
          </h2>
          <h2>
              <p>upper</p>
              <p>
                  {{title|upper}}
              </p>
          </h2>
          <h2>
              <p>title</p>
              <p>
                  {{title|title}}
              </p>
          </h2>
      </body>
      </html>
      
  3. 標籤

    1. 什麼是標籤

      每個標籤表示的是不同服務器端的功能

    2. 常用標籤

      1. if 標籤

        1. 基本 if 結構

          {% if 條件 %}

          {% else %}

          {% endif %}

      2. for 結構

        {% for 變量 in 列表 | 元組 | 字典 %}

        {% endfor %}

        內置變量:loop

        1.只能在for循環標籤中使用

        2.不用申明直接使用

        loop中的常用屬性:

        1. index

          作用:記錄當前循環的次數,從1開始計算

        2. index0

          作用:記錄當前循環的次數,從0開始計算

        3. first

          作用: 表示當前的循環是否爲第一次循環

          True:表示爲第一次循環

          False:表示不是第一次循環

        4. last

          作用:表示當前循環是否爲最後一次循環

          True:表示爲最後一次循環

          False:表示不是最後一次循環

      3. macro 標籤(宏)

        1. 作用

          相當於是在模板中聲明函數

        2. 語法

          {% macro 名稱(參數列表) %}

          {% endmacro %}

          使用宏:

          {{宏名稱(參數列表)}}

        3. 在獨立的模板文件中聲明所有的宏

          1. 創建 macro.html 模板文件

            作用:定義項目中要用到的所有的宏

          2. 在使用宏的模板上,導入 macro.html

            {% import ‘macro.html’ as macros %}

  4. 現在展示一下標籤在模板中的用法

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {% for key in params.dic.keys() %}
            <h2>
                {{key}}:{{params.dic[key]}}
            </h2>
        {% endfor %}
        <!--
                如果是第一次循環,將h2的背景改爲red色
                如果是最後一次循環,將h2的背景改爲orange色
                否則,將h2的背景改爲 pink 色
         -->
        {% if loop.first %}
        	<h2 style="background:red;">{{key}}:{{value}}</h2>
        {% elif loop.last %}
        	<h2 style="background:orange;">{{key}}:{{value}}</h2>
        {% else %}
        	<h2 style="background:pink;">{{key}}:{{value}}</h2>
        {% endif %}
    

    macro在模板中的用法

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <!-- 聲明宏:接收一個字符串作爲參數,將參數放p中打印輸出 -->
        {% macro showP(str) %}
            <h3>使用宏顯示的內容:</h3>
            <p>{{str}}</p>
        {% endmacro %}
    
        <!-- 使用宏 -->
        <div>
            {% for name in params.list %}
                {{showP(name)}}
            {% endfor %}
        </div>
        <!-- 引入外部的宏 -->
        {% import 'macros.html' as ms %}
        {% for name in params.list %}
            {{ms.show_p(name)}}
        {% endfor %}
    </body>
    </html>
    

    5.模板的繼承

    1. 什麼是模板的繼承

      模板的繼承類似於類的繼承,可以複用重複的代碼,簡化代碼

    2. 語法

      1. 父模板中

        需要在父模板中定義出哪些內容在子模板中是可以被重寫的

        {% block 塊名 %}

        {% endblock %}

        block:

        1. 在父模板中正常顯示,沒有任何影響
        2. 在子模板中可以被重寫
      2. 子模板中

        1. 使用 {% extends ‘父模板名稱’ %} 來表示繼承

        2. 使用 {% block 塊名%} 來重寫父模板中同名塊的內容

          {% block 塊名 %}

          會覆蓋父模板中同名的block的內容

          {% endblock %}

請求(request) 和 響應(response)

  1. http 協議

    1. Request:請求消息
      1. 請求行
        • 請求方式
        • 請求路徑
        • 協議以及版本號
      2. 請求頭
      3. 請求體:由客戶端瀏覽器帶給服務器的數據或信息,只有post和put請求才有請 求主體
    2. Response:響應消息
      1. 響應行
        • 協議和版本號
        • 響應狀態碼
        • 原因短句
      2. 響應頭
      3. 響應體
  2. request - 請求對象

    1. 什麼是請求對象

      request - 請求對象,封裝了所有與請求相關的信息在Flask中,可以通過 request 對象 來獲取請求信息
      from flask import request

    2. request中的常用成員

      1.scheme:獲取請求方案(協議)
      2.method:獲取請求方式(重點,取值爲 post 或 get)
      3.args : 獲取使用get請求方式提交過來的數據(重點)
      4.form : 獲取使用post請求方式提交過來的數據(重點)
      5.cookies : 獲取cookies中的相關信息
      6.headers : 獲取請求消息頭的相關信息
      7.files : 獲取上傳的文件
      8.path : 獲取請求的資源的具體路徑(不帶參數)
      9.full_path : 獲取完整的請求資源的具體路徑(帶參數)
      10.url : 獲取完整的請求地址,從協議開始

  3. 獲取請求的信息

    from flask import Flask, render_template
    from flask import request
    # 所有的請求信息都封裝在Flask的request中
    
    app = Flask(__name__)
    
    @app.route('/03-request')
    def request_views():
        # print(dir(request))
        # 獲取請求方案(協議)
        scheme = request.scheme
        # 獲取請求方式
        method = request.method
        # 獲取使用get請求方式提交過來的數據
        args = request.args
        # 獲取使用post請求方式提交過來的數據
        form = request.form
        # 獲取cookies中的相關信息
        cookies = request.cookies
        # 獲取請求消息頭的相關信息
        headers = request.headers
        # 獲取請求資源的路徑(不帶參數)
        path = request.path
        # 獲取請求資源的路徑(帶參數)
        full_path = request.full_path
        # 獲取請求路徑
        url = request.url
        # 獲取請求源地址
        referer = request.headers.get('Referer','/')
        return render_template('03-request.html',params=locals())
    
    @app.route('/06-form',methods=['POST','GET'])
    def form06_views():
        if request.method == 'GET':
            return render_template('06-form.html')
        else:
            uname = request.form['uname']
            upwd = request.form['upwd']
            uemail = request.form['uemail']
            tname = request.form['tname']
    
            print(uname,upwd,uemail,tname)
            return "Post OK
    
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'),404
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0')
    
  4. reponse-響應

    1. 什麼是響應

      響應就是由服務器端帶給客戶端的內容

      響應內容可以是字符串,模板,重定向

    2. 響應字符串 或 模板

      1. 響應字符串

        return “普通字符串”

      2. 響應模板

        return render_template(‘xxx.html’)

      3. 響應對象

        將響應內容封裝到一個對象中,以便完成更多的響應行爲

        from flask import make_response
        
        @app.route('/xxx')
        def xxx():
            resp = make_response('響應內容')
            resp = make_response(render_template('xxx.html'))
            #允許調用resp中的屬性或方法們以便完成更多的響應行爲
            return resp
        
      4. 重定向

        1. 什麼是重定向

          由服務器通知客戶端重新向新的地址發送請求

          from flask import redirect
          
          @app.route('/xxx')
          def xxx():
              xxxx xxxx 
              return redirect('重定向地址')
          

Models-模型

  1. 通過SQLAlchemy創建實體類到數據庫中表的映射

  2. class MODELNAME(db.Model):
        __tablename__ = "TABLENAME"
        COLUMN_NAME=db.Column(db.TYPE,OPTIONS)
          
          # 1.MODELNAME : 定的模型類的類名,通常參考表名
          # 2.TABLENAME : 映射到數據庫中表的名稱
          # 3.COLUMN_NAME : 屬性名,映射到數據表中字段名
          # 4.TYPE : 映射到列的數據類型
          # 5.OPTIONS : 列選項
    
          db.TYPE 列的數據類型:
          類型名         python類型       說明
          Integer       int              普通整數,32位
          SmallInteger  int              小範圍整數,16位
          BigInteger    long             不限精度的整數
          Float         float            浮點類型
          Numeric       decimal.Decimal  定點類型
          String        str              變長字符串
          Text          str              變長字符串
          Boolean       bool             布爾值
          Date          datetime.date    日期
          Time          datetime.time    時間
          DateTime      datetime.datetime日期和時間
    
          OPTIONS 列選項
          選項名           說明
          primary_key      如果設置爲True表示該列爲主鍵
          unique           如果設置爲True表示該列的值唯一
          index            如果設置爲True表示該列要創建索引
          nullable         如果設置爲True表示該列允許爲空,默認允許爲空
          default          指定該列的默認值
    
  3. 查詢

     # 語法
     db.sessin.query(Models1,Models2,...).過濾器函數().執行函數()
     
     1.查詢過濾器函數
         作用:專門對數據進行篩選,返回查詢對象
         1.filter()    按指定條件進行過濾(單表,多表,定值,不定值)
         2.filter_by() 按等值條件進行過濾
         3.limit()     按限制行數量獲取結果
         4.order_by()  按指定列進行排序
         5.group_by()  按指定條件進行分組
         
    2. 查詢執行函數
        目的:在query()的基礎上得到最終的數據
        1.all():以列表的方式返回query對象中所有的查詢數據
        2.first():返回query對象中的第一個查詢結果,如果沒有結果,返回None
        4.count():返回query對象中的查詢結果的數量
        
    實例:
    過濾器函數詳解:
    1.filter()
    注意:條件必須由 實體類.屬性 組成
    1.查詢年齡大於 30 的人的信息
    	db.session.query(Users).filter(Users.age>30).all()
    2.查詢id爲1的人的信息
    	db.session.query(Users).filter(Users.id==1).first()
    注意:filter()做等值判斷時必須使用 == 
    3.查詢年齡大於30並且id大於1的用戶的信息
    	filter(條件1,條件2,...) : and
    	db.session.query(Users).filter(Users.age>30,Users.id>1).all()
    4.查詢年齡大於30或者id爲1的用戶的信息
    	filter(or_(條件1,條件2))
    	from sqlalchemy import or_
    	db.session.query(Users).filter(or_(Users.age>30,Users.id==1)).all()
    5.查詢 email 中包含 'w' Users的信息
        db.session.query(Users).filter(Users.email.like('%w%'))
    6.查詢 id 在 [2,4] 列表中的Users的信息
    	db.session.query(Users).filter(Users.id.in_([2,4])).all()
    	
    2.filter_by()
    注意:只能做等值判斷,不能做不等值
    1.查詢id爲1的users
    	db.session.query(Users).filter_by(id=1).first()
    	
    3.limit()
    1.獲取 users 表中的前2條數據
    	select * from users limit 2
    	db.session.query(Users).limit(2).all()
    2.獲取 users 表中過濾前3條數據後剩餘的前2條數據
    	select * from users limit 3,2
    	db.session.query(Users).limit(2).offset(3).all()
    
    4.order_by()
    1.id 倒序排序
    	select * from users order by id desc;
    	db.session.query(Users).order_by("id desc").all()
    2.年齡倒序排序,再按照id升序排序
    	select * from users order by age desc,id asc;
    	db.session.query(Users).order_by("age desc,id asc").all()
    	
    5.group_by()
    1.將 users 表中的數據按照 age 進行分組
    	db.session.query(Users.age).group_by('age').all()
    	
    6.聚合函數
    1.查詢users表中所有人的平均年齡
    	select avg(age) as avgAge from users;
    	
    	from sqlalchemy import func
    	db.session.query(func.avg(Users.age).label('avgAge')).all()
    2.users表中,按年齡分組,再查每組的年齡平均值
    	select age,avg(age) from users group by age
    
    	db.session.query(User.age,func.avg(Users.age).label('avgAge')).group_by('age').all()
    
    聚合函數:
    1.func.avg() : 求平均值
    2.func.sum() : 求和
    3.func.max() : 求最大值
    4.func.min() : 求最小值
    5.func.count() : 求不爲空的數量
    
  4. 關係映射

    1. 一對多關係映射

      1. 在 “多” 實體類中

        外鍵列名 = db.Column(db.Integer,db.ForeignKey(‘主表.主鍵’))

      2. 增加關聯屬性以及反向引用關係(relationship)

        屬性名=db.relationship(‘多表實體類名’,關係選項)

        關係選項:
        選項名 說明
        backref 添加的反向引用屬性名
        lazy 指定如何加載當前的相關記錄(延遲加載模式)
        select:首次訪問時加載所有記錄
        immediate:源對象只要加載就馬上加載相關記錄
        subquery:效果同上,使用子查詢的方式加載記錄
        noload:永不加載相關記錄
        dynamic:默認也不加載相關記錄,但提供記錄的查詢
        uselist 如果設置爲False,表示不使用列表表示相關數據而使用標量
        secondary 指定多對多關係中關聯表的名字

    2. 多對多關係映射

      1. 創建第三張關聯表

      2. 在 “多” 實體類中

        屬性名 = db.relationship("'多表實體類名",secondary=“關聯表的名字”,backref=“反向引用屬性名”, lazy=“dynamic”)

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    import pymysql
    pymysql.install_as_MySQLdb()
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI']="mysql://root:123456@localhost:3306/flask"
    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True
    
    db = SQLAlchemy(app)
    
    class Users(db.Model):
        __tablename__ = 'users'
        id = db.Column(db.Integer,primary_key=True)
        username = db.Column(db.String(80),nullable=False,unique=True)
        age = db.Column(db.Integer)
        email = db.Column(db.String(120),unique=True)
        isActive = db.Column(db.Boolean,default=True)
    
        # 添加多(Users)對多(Goods)的關聯屬性和反向引用關係
        # 涉及到第三張關聯表 - users_goods
        goods=db.relationship(
            'Goods',
            secondary='users_goods',
            lazy="dynamic",
            backref=db.backref(
                "users",
                lazy='dynamic')
        )
    
        #增加對UsersGoods的關聯屬性和反向引用關係:目的是爲了創建Users類 與 UsersGoods類 之間的關係
        userGoods = db.relationship('UsersGoods',backref="user",lazy="dynamic")
    
    
    class Goods(db.Model):
        __tablename__ = "goods"
        id = db.Column(db.Integer,primary_key=True)
        gname = db.Column(db.String(80))
        gprice = db.Column(db.Float)
    
        #增加對UsersGoods類的關聯屬性和反向引用關係
        goodUsers = db.relationship('UsersGoods',backref="good",lazy="dynamic")
    
    # 創建 users_goods 的第三張關聯表,從而來表示多對多的關係
    class UsersGoods(db.Model):
        __tablename__ = "users_goods"
        id = db.Column(db.Integer,primary_key=True)
        users_id = db.Column(db.Integer,db.ForeignKey('users.id'))
        goods_id = db.Column(db.Integer,db.ForeignKey('goods.id'))
        count = db.Column(db.Integer,default=1)
    
    db.create_all()
    
    @app.route('/01-users-goods')
    def users_goods_views():
        #爲1號用戶購買1號商品
        # user=Users.query.filter_by(id=1).first()
        # good=Goods.query.filter_by(id=1).first()
        #將good商品增加到user所購買的商品列表中
        # user.goods.append(good)
        #將user更新回數據庫
        # db.session.add(user)
    
    
    
        #爲1號用戶購買2號商品
        ug = UsersGoods()
        ug.users_id = 1
        ug.goods_id = 2
        ug.count = 5
        db.session.add(ug)
        return "OK"
    
    @app.route('/02-remove-goods')
    def remove_goods():
        # 獲取 id 爲1 的Users信息
        user = Users.query.filter_by(id=1).first()
        # 獲取 id 爲1 的Goods信息
        good = Goods.query.filter_by(id=1).first()
        # 將good從user中移除出去
        user.goods.remove(good)
        db.session.add(user)
        return "Remove OK"
    
    @app.route('/03-query-goods')
    def query_goods():
        # 查詢2號用戶購買的商品
        user = Users.query.filter_by(id=2).first()
        # print(type(user.goods))
        goods = user.goods.all()
        print('用戶姓名:%s' % user.username)
        for g in goods:
            print("商品名稱:%s" % g.gname)
            #查詢每個商品的購買數量
            count=user.userGoods.filter_by(goods_id=g.id).first().count
            print("購買數量:%d" % count)
        print('*****************************')
        # 購買2號商品的用戶們
        good = Goods.query.filter_by(id=2).first()
        users = good.users.all()
        print("商品名稱:%s" % good.gname)
        for u in users:
            print("用戶名稱:%s" % u.username)
            #獲取購買good商品的u們的購買數量count
            count=good.goodUsers.filter_by(users_id=u.id).first().count
            print('購買數量:%d' % count)
        return "Query OK"
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0')
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章