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')
    

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