Flask初识

发布时间 2023-04-03 00:44:36作者: lsumin

Flask初识

今日内容

1 Flask和pythonweb框架介绍

# python web框架 本质都一样
	django:大而全 内置的app多 第三方的app也多
    flask:小而精 没有过多的内置组件 只完成web框架最基本的功能 需要借助于第三方 完成更丰富的功能
    web.py:是一个小巧灵活的python框架 它简单而且功能强大(国内几乎没有用的)
    -----------异步web框架------------------
    fastapi:python的异步web框架 不少公司在用https://fastapi.tiangolo.com/zh/
    sanic:python的异步web框架 供支持异步高并发请求的web服务
    tornado:异步框架 用的比较少了
    
# 同步框架和异步框架的区别
	django是同步框架还是异步框架 django3.x以后支持异步
    同步框架的意思:一个线程只处理一个请求
    同步框架的意思:一个线程可以处理多个请求
    异步框架可以很显著的提高并发量

1.1 flask介绍

Flask是一个基于python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架
	jinja2 模板语法 django的dtl 非常像
    Werkzeug WSGI 符合wsgi协议的web服务器 django使用的是wsgiref
    
### wsgif写web
from wsgiref.simple_server import make_server


def mya(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    if environ.get('PATH_INFO') == '/index':
        with open('index.html', 'rb') as f:
            data = f.read()

    elif environ.get('PATH_INFO') == '/login':
        with open('login.html', 'rb') as f:
            data = f.read()

    else:
        data = b'<h1>Hello, web!</h1>'

    return [data]  # 做成了response


if __name__ == '__main__':
    myserver = make_server('', 8008, mya)  # 第一个参数是监听地址 不写就是本机127.0.0.1
    print('监听8008')
    myserver.serve_forever()
    
    
### 使用werkzeug写web

2 flask快速使用

# 安装:pip install flask  安装依赖:MarkupSafe Werkzeug jinja2 flask
	1.x 没有本质区别
    2.x 没有本质区别 源码上动了 用起来一样
from flask import Flask


app = Flask(__name__)  # 这个里面可以放任意字符串


# 注册路由---> 装饰器
@app.route('/index')
def index():
    return 'hello web'


@app.route('/')
def home():
    return 'hello home'


if __name__ == '__main__':
    # app.run('127.0.0.1', 5000)
    app.run()  # 默认在本地5000端口

3 登录 显示用户信息小案例

3.1 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form method="post">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="password" name="password"></p>
    <input type="submit" value="登录"> {{error}}
</form>
</body>
</html>

3.2 home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
    {% for k,v in user_dict.items() %}
    <tr>
        <td>{{k}}</td>
        <td>{{v.name}}</td>
        <td>{{v['name']}}</td>
        <td>{{v.get('name')}}</td>
        <td><a href="/detail/{{k}}">查看详细</a></td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

3.3 detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>名字是:{{user.name}}</p>
<p>年龄是:{{user['age']}}</p>
<p>性别是:{{user.get('gender')}}</p>
<p>{{user.text}}</p>


</body>
</html>

3.4 py文件

from flask import Flask, request, render_template, redirect, session,jsonify

app = Flask(__name__)

# 要使用session,必须设置秘钥,秘钥是配置信息
app.secret_key = 'asdfasdfa33aef3aefads'

USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
    3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}
# 1 创建templates文件夹,写login.html
@app.route('/login', methods=['GET', 'POST'])
def index():
    # 没有request对象,使用全局的request
    # get请求,返回模板
    if request.method == 'GET':
        return render_template('login.html')  # 新手四件套之一:返回模板
    else:
        # post请求,校验数据
        # 取出前端传入的用户名密码,校验
        username = request.form.get('username')  # 等同于django的的request.POST
        password = request.form.get('password')
        if username == 'lqz' and password == '123':
            # 登录成功,保存登录状态 重定向到跟路径   新手四件套之一:重定向
            # 保存到session中,session是全局的
            session['name'] = username
            return redirect('/')
        else:
            return render_template('login.html', error='用户名或密码错误')  # 注意跟django的render区分,要模板渲染的数据,直接key=value传即可


@app.route('/')
def home():
    # 校验,登录成功,才能过来,不登录,重定向到登录页面
    if session.get('name'):  # 有值说明登录了,没有值说明没有登录
        return render_template('home.html',user_dict=USERS)
    else:
        return redirect('/login')


@app.route('/detail/<int:pk>')
def detail(pk):
    if session.get('name'):  # 有值说明登录了,没有值说明没有登录
        user_detail = USERS.get(pk)
        return render_template('detail.html', user=user_detail)
    else:
        return redirect('/login')


@app.route('/test')
def test():
    return jsonify([{'name':'lqz','age':19}])
if __name__ == '__main__':
    app.run()



'''
# 学到的
    1 注册路由  app.route(路径,methods=[请求方式get,post])
    2 新手四件套:
        -render_template   渲染模板 跟django有区别
        -redirect  重定向
        -return 字符串 返回字符串
        -jsonify 返回json格式
        
    3 请求的request对象,是全局的,直接导入使用即可,在不同视图函数中不会混乱
        request.method  请求方式
        request.form   post请求的body体的内容转成了字典
        
    4 session 全局的,直接导入使用即可,一定要指定秘钥app.secret_key = 'asdfasdfa33aef3aefads'
        放值:session['name']='lqz'
        取值:session.get('name')
        
    5 模板的渲染
        -兼容django的dtl
        -更强大,可以加括号,字典可以.get  .values()   .items()
        -{% for %}
    
    6 转换器@app.route('/detail/<int:pk>')

'''

4 配置文件方式

# django 有个settings
# flask 也有配置问题,但是它的使用方式多种:
	# 设置的方式一:(测试用)
    # app.debug=True  # 调试模式,提示信息更详细,修改代码不需要重启,自动重启
    # app.secret_key='dasdfasdfasd'  # 秘钥,只能 放debug和secret_key

    ## 设置方式二:直接使用app.config设置
    # app.config['DEBUG']=True
    # app.config['SECRET_KEY']='sdfasdfasd'
    # print(app.config)


    ## 方式三:使用py文件(不常用)
    # app.config.from_pyfile("settings.py")
    # print(app.config)

    ## 方式四:常用的,使用类的方式
    # app.config.from_object('settings.DevelopmentConfig')
    # app.config.from_object('settings.ProductionConfig')
    # print(app.config)

    ### 其他:
    #通过环境变量配置
    # app.config.from_envvar("环境变量名称")

    # json
    # app.config.from_json("json文件名称")
    # JSON文件名称,必须是json格式,因为内部会执行json.loads


    # 字典格式---》配置中心
    # app.config.from_mapping({'DEBUG': True})
   



# 内置的配置字段,其他可以写自己的,比如 redis的连接地址,mysql的连接地址
	-DEBUG
    -SECRET_KEY
    -SESSION_COOKIE_NAME
    -PERMANENT_SESSION_LIFETIME
# settings内文件

# DEBUG=True
# SECRET_KEY='00sdfasf0asdf'


class BASE(object):
    DEBUG = False


class ProductionConfig(BASE):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(BASE):
    DEBUG = True
    DATABASE_URI = 'localhost'

5 路由系统

5.1 路由本质

# django中配置路由 在url.py中 写path 写在列表中
# flask是基于装饰器 大部分都用装饰器来做 少量可以抽取到概要urls.py中

# 路由的装饰器源码分析
	# 咱们这样写
    @app.route('/login')
    def index():
        pass
    
# 本质是---> index=app.route('/login')(index)

# app.route('/login')的执行结果 decorator 函数
	-rule是路径
    -其他参数给options
    
# 然后decorator(index)---> 在执行
	# f 是 index
    endpoint = options.pop("endpoint", None) # 目前没有endpoint,是None
    # 核心 本周---> self就是实例化得到的app对象 flask对象
    # app对象中有个方法add_url_rule 这是在添加路由
    # 不使用装饰器 自己注册路由
    self.add_url_rule(rule, endpoint, f, **options)
    return f

    def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
        def decorator(f: T_route) -> T_route:
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        
        return decorator
    
# 可以不使用装饰器的方式 注册路由
	app.add_url_rule('/', endpoint=None, view_func=home, methods=['GET'])
    
# flask路由的本质是app对象的add_url_rule完成路由的注册

5.2 路由参数add_url_rule

# rule  URL规则
# view_func  视图函数名称
# defaults = None  默认值 当URL中无参数 函数需要参数时 使用defaults = {'k': 'v'} 为函数提供参数
# endpoint = None  路径的别名 名称 用于反向解析URL 即:url_for('名称')
# methods = None  允许的请求方式 如:['GET', 'POST']

#对URL最后的 / 符号是否严格要求
strict_slashes = None
    '''
        @app.route('/index', strict_slashes=False)
        #访问http://www.xx.com/index/ 或http://www.xx.com/index均可
        @app.route('/index', strict_slashes=True)
        #仅访问http://www.xx.com/index
    '''

#重定向到指定地址
redirect_to = None, 
    '''
        @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
    '''
    
    
# 需要记住的
	# rule
    # view_func
    # defaults
    # endpoint
    # methods

5.3 转换器

 'default':          UnicodeConverter,
 'string':           UnicodeConverter,
 'any':              AnyConverter,
 'path':             PathConverter,
 'int':              IntegerConverter,
 'float':            FloatConverter,
 'uuid':             UUIDConverter,
    
 # 了解:让路由支持正则(忽略掉)

扩展

1 数据库三大范式是什么

# 1 为什么需要数据库设计
设计数据库的时候 要考虑很多的问题:
- 用户需要哪些数据 我们在数据表中要保存哪一些数据
- 怎么保证数据表中的数据的正确性
- 如何降低数据表的冗余度
- 开发人员怎么才能更方便的使用数据库

如果数据库设计的不合理的话 可能导致下面的几种问题:
- 设计容易 信息重复 存储空间浪费
- 数据更新 插入 删除的异常
- 不能正确表示信息
- 丢失有效信息
- 程序性能差

设计良好的数据库很重要 它有下面的优点:
- 节省数据的存储空间
- 能够保证数据的完整性
- 方便进行数据库应用系统的开发

设计数据库 我们得重视数据表的设计 为了建立冗余度小 结构合理的数据库 设计数据库必须遵循一定的规则

# 2 范式概述
关系型数据库中 关于数据表设计的基本原则 规则就称为范式 范式是我们在设计数据库结构过程中需要遵守的规则和指导方法

# 3 第一范式
- 第一范式主要是保证数据表中的每一个字段必须具有原子性 也就是数据表中的每个字段的值是不可拆分的最小数据单元

- ***属性的原子性是主观的 我们要根据实际项目的需求来设计 比如说地址 如果项目没有说要细分为省市区这么具体的话 我们一般就可以不拆分***

# 4 第二范式
- 第二范式要求在满足第一范式的基础上 还要满足***数据表里的每一条数据记录 都是可唯一标识的 而且所有的非主键字段 都必须完全依赖主键 不能只依赖主键的一部分***

- 如果知道主键的所有属性的值 我们就可以检索任意元组(行)的任何属性的任何值(要求中的主键可以拓展替换为候选键)

- ***第二范式要求实体的属性完全依赖主关键字 如果存在不完全依赖 那么这个属性和主关键字的这一部分就应该分离处理形成应该新的实体 新实体和原来的实体之间是一对多的关系***
# 5 第三范式
- 第三范式建立在已经满足第二范式的基础上
- 数据表中的每一个非主键字段都和主键字段直接相关
- ***也就是说数据表中的所有非主键字段不能依赖于其他非主键字段***
- ***这个规则的意思是所有非主键之间不能有依赖关系 它们是相互独立的
- 这里的主键可以拓展成为候选键

# 6 范式的优缺点
- 优点:
	数据的标准化有助于消除数据库中的数据冗余
- 第三范式通常被认为在性能 扩展性和数据完整性方面达到了最好的平衡
- 缺点:
	降低了查询效率 因为范式等级越高 设计出来的表就越多 进行数据查询的时候就可能需要关联多张表 不仅代价昂贵 而且可能会使得一些索引失效
    范式只是提出设计的标准 实际设计的时候 我们可能为了性能和读取效率违反范式的原则 通过增加少量的冗余或重复的数据来提高数据库的读取性能 减少关联查询 实现空间换时间的目的
# 还可以扩展的
# 7 反范式化
# 8 BCNF(巴斯范式)

2 mysql有哪些索引类型 分别有什么用

# 主键索引
数据列不允许重复 不允许为null 可以被引用为外键 一个表只能有一个主键索引

# 唯一索引
数据列不允许重复 不允许为null 不可以被引用为外键 一个表允许多个列创建唯一索引

# 普通索引
基本的索引类型 没有唯一性限制 允许为null值 不可以被引用为外键 一个表可以有多个普通索引

# 聚集索引(聚簇索引)
在聚集索引中 表中数据行的物理位置与逻辑值(索引和数据为同一个文件)的顺序相同 一个表中只能包含一个聚集索引 因为物理顺序只能有一个 聚集索引通常提供更快的数据访问速度

# 非聚簇索引
索引和数据分开的索引

# 覆盖索引
所谓覆盖索引就是指索引中包含了查询中的所有字段 这种情况下就不需要再进行回表查询了

# 组合索引
使用多个列来组成一个索引

# 全文索引
主要用于海量数据的搜索 比如淘宝 京东对商品的搜索就可以建立全文索引
适用场景:适用于海量数据的关键字模糊搜索 比如简易版的搜索引擎
索引的实现方式

# B-Tree索引
InnoDB使用的是B-Tree算法 
即每个叶子节点包含指向下一个叶子节点的指针 就像一个树一样
适用场景:最常见的一个索引类型 可以适用于多种场景

# 哈希索引
如果在列上建立索引 则针对每一行数据 存储引擎会根据所有的所有列计算出一个哈希码 每一个行计算出的哈希码会组成一个哈希表 同时在哈希表中存储了指向每个数据行的指针
适用场景:仅作等值匹配且数据重复率低且对索引查找速度要求高的情况

# 空间数据索引(R-Tree)
空间索引可用于地理数据存储 它需要GIS相关函数的支持 由于MySQL的GIS支持并不完善 所以该索引方式在MySQL中很少有人使用
可以拓展的
# 回表
# 如果避免回表
# 索引下推
# 唯一索引导致死锁
有三个事务同时插入同一个记录,导致唯一索引冲突的过程:

死锁发生过程:

T1时刻sessionA插入会加排他锁
T2时刻SessionB插入同id会主键冲突,会加上共享锁
T3时刻SessionC插入同id会主键冲突,会加上共享锁
这时,SessionA回滚释放排他锁,sessionB向获得排他锁,但发现sessionC有共享锁存在,B和C相互等待造成死锁
根本原因:
唯一索引导致,本质是并发请求导致一个数据重复插入或是网络抖动造成

解决方案:

可以使用缓存将重复请求去重,确保同时只执行一个相同sql
异常捕捉,mysql有死锁检测和恢复,只有一个事务会成功,只需要catch异常即可
————————————————
版权声明:本文为CSDN博主「我思知我在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32828253/article/details/109630546

3 事务的特性和隔离级别

# 事务的四个特性(简称ACID):原子性 一致性 隔离性 持久性
# 事务的隔离级别:读未提交(1000) 读已提交(1100) 可重复读(1110) 串行化(1111)

# 1 事务四个特性
如果一个数据库声称支持事务的操作 那么该数据库必须要具备以下四个特性:
## 1 原子性(Atomicity)
	原子性是指事务包含的所有操作要么全部成功 要么全部失败回滚 因此事务的操作如果成功就必须要完全应用到数据库 如果操作失败则不能对数据库有任何影响
    
## 2 一致性(Consistency)
	一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态 也就是说一个事务执行之前和执行之后都必须处于一致性状态
    转账举例:两者钱总数加起来必须总量保持一致
    
## 3 隔离性(Isolation)
	隔离性是当多个用户并发访问数据库时 比如操作同一张表时 数据库为每一个用户开启的事务 不能被其他事务的操作所干扰 多个并发事务之间要相互隔离
    即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
## 4 持久性(Durability)
	持久性是指一个事务一旦被提交了 那么对数据库中的数据的改变就是永久性的 即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
    例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
————————————————
版权声明:本文为CSDN博主「Evan Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41378597/article/details/88377509
# 读未提交(Read Uncommitted)
含义解释:只限制同一数据写事务禁止其他写事务 解决"更新丢失"(一事务成写时禁止其他事务写)
名称解释:可读取未提交数据
所需的锁:排他写锁

# 读提交(Read Committed)
含义解释:只限制同一数据写事务禁止其他读写事务 解决"脏读" 以及"更新丢失"(一事务写时禁止其他事务写)
名称解释:必须提交以后的数据才能被读取
所需的锁:排他写锁 瞬间共享锁

# 可重复读(Repeatable Read)
含义解释:限制同一数据写数据禁止其他其他读写事务 读事务禁止其他写事务(允许读) 解决"不可重复读" 以及"更新丢失"和"脏读"(一事务写时禁止其他事务读写 一事务读时禁止其他事务写)
'''注意没有解决幻读 解决幻读的方法是增加范围锁(range lock)或者表锁'''
名称解释:能够重复读取
所需的锁:排他写锁 共享读锁

# 串行化(Serializable)
含义解释:限制所有读写事务都必须串行化实行 他要求事务序列化执行 事务只能一个接一个地执行 但不能并发执行 如果仅仅通过"行级锁"是无法实现事务序列化的 必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到(一事务写时禁止其他读写 一事务读时禁止其他事务读写)
所需的锁:范围锁或表锁

可扩展
事务隔离
不事务隔离带来的问题
更新丢失 脏读 不可重复读 幻读