其他分享
首页 > 其他分享> > 新篇章---flask---3

新篇章---flask---3

作者:互联网

目录

新篇章---flask---3

一 CBV

1.1 概念

参考django中的cbv和FBV,赫然发现,没啥区别. 框架的东西应该都是差不多的,,吧. CBV是(class base views) ,可以自定义属性方法.

rest API的话更建议使用CBV的方式.

导入方式,from flask.views import MethodView,然后写app.add_url_rule('/login', endpoint=None, view_func=Login.as_view('/login')) #本来写的是login,但是经过测试又报了名字重复的错误导入即可.

1.2 实例

from flask.views import MethodView


class Login(MethodView):
    def get(self):
        pass

print(Login.__module__)
print(Login.__class__)

app.add_url_rule('/login', endpoint=None, view_func=Login.as_view('/login')) #本来写的是login,但是经过测试又报了名字重复的错误

if __name__ == '__main__':
    app.run('0.0.0.0', 5000, debug=True)

1.3 其他补充

1.3.1 flask的return值有几种?

答:5种.

  1. Flask中的HTTPResponse ---在Flask 中的HttpResponse就是直接返回字符串
  2. Flask中的Redirect ---自动重定向
  3. Flask 中的 render (render_template)---里面涉及MarkupSave函数的使用
    注意: 如果要使用 render_template 返回渲染的模板,请在项目的主目录中加入一个目录 templates----否则可能会有一个Jinja2的异常
  4. 打开并返回文件内容 send_file("文件的路径")---(最新版本也可以直接返回f"字符串"(字符串可以用{写入想写入的变量}))
  5. 标准JSON格式的Response jsonify({name:1}) # Content-type:application/json

    1.3.2 HTTP的请求方式有8种

    http的请求方式至少包括增删改查要会!!!
from flask.views import MethodView

class Login(views.MethodView):
    #http请求方式 有8种
    def get(self):     # 获取
        pass
    def post(self):    # 增
        pass
    def delete(self):   # 删除
        pass
    def put(self):    # 更新
        pass

对应的是增删改查.

HTTP的请求方式有8种---示例1
HTTP的请求方式有8种---示例2

1.3.3 app.run('0.0.0.0')

0.0.0.0 有几个不同的含义,不过当告诉服务器监听了 0.0.0.0,意味着监听每一个可用的网络接口,从服务器进程的角度来看,IP 地址为 127.0.0.1 的环回适配器看起来就像机器上的任何其他网络适配器一样,因此被告知监听 0.0.0.0 的服务器也将接受该接口上的连接。
因此在实际应用中,一般我们在服务端绑定端口的时候可以选择绑定到 0.0.0.0,这样我的服务访问就可以通过主机的多个 ip 地址访问我的服务。

私有地址与公有地址不同,并不是由Internet分配的,是不允许出现在Internet中的,我们在公网中是看不到私有IP地址的,并且公有地址也不会使用上述的三类地址。所以,私有地址是不能直接与Internet连接的。

而如果想用私有地址与Internet连接来访问公网,那该怎么做?这就需要将私有IP地址转换成公网IP地址,与外部连接。所以,我们平时使用的路由器中会装有一个叫做 NAT(网络地址转换) 的软件,我们的路由器中会至少会有一个有效的公网IP,NAT会将我们的私有地址转成路由器中的公网IP与外部Internet连接。而同样的,因为使用的是路由器中的公共的公网IP来连接Internet,所以这个内网中的PC在Internet中显示的都是路由器的公共IP,这样做不仅提供了一定程度的安全,也可以有效的减缓可用的IP地址空间的枯竭问题。(像我们学校或者公司的内网一般都是这么做的)

另外还有一点,在同一个局域网内,IP地址是唯一的;但是在不同的局域网内,IP地址是可以重复出现的。

几个域名的比较:
localhost:
localhost其实是域名,一般windows系统默认将localhost指向127.0.0.1,但是localhost并不等于127.0.0.1,localhost指向的IP地址是可以配置的
127.0.0.1:
首先我们要先知道一个概念,凡是以127开头的IP地址,都是回环地址(Loop back address),其所在的回环接口一般被理解为虚拟网卡,并不是真正的路由器接口。
所谓的回环地址,通俗的讲,就是我们在主机上发送给127开头的IP地址的数据包会被发送的主机自己接收,根本传不出去,外部设备也无法通过回环地址访问到本机。

小说明:正常的数据包会从IP层进入链路层,然后发送到网络上;而给回环地址发送数据包,数据包会直接被发送主机的IP层获取,后面就没有链路层他们啥事了。

0.0.0.0:(监听所有端口)
首先,0.0.0.0是不能被ping通的。在服务器中,0.0.0.0并不是一个真实的的IP地址,它表示本机中所有的IPV4地址。监听0.0.0.0的端口,就是监听本机中所有IP的端口。
本机IP:
本机IP通常仅指在同一个局域网内,能同时被外部设备访问和本机访问的那些IP地址(可能不止一个)。像127.0.0.1这种一般是不被当作本机IP的。本机IP是与具体的网络接口绑定的,比如以太网卡、无线网卡或者PPP/PPPoE拨号网络的虚拟网卡,想要正常工作都要绑定一个地址,否则其他设备就不知道如何访问它。

二 回顾flask基础

2.1 flask的优势和劣势

flask优势:
轻量级框架,扩展性,三方组件全

flask劣势:
太轻了(框架),只有个session,轻到连内存都能接受;
组件是第三方的,稳定性相对较差.

2.2 依赖包

pip3 install flask
安装完flask的时候就会全部安装.

Flask 源码
Jinja2 模板语言
MarkupSafe 处理标签语言 render_template里面应用了MarkupSafe,否则就是输出txt了
Werkzeug 德语工具的意思,承载flask程序werkzeug约等于UWSGI,几乎一样(本质都是wsgi,前者更轻,但是不如后者稳定,有一个run_simple(),也就是app.run())

2.3 启动Flask

2.3.1 flask的简单启动

from flask import Flask

app = Flask(__name__)

app.run('0.0.0.0',9527)  # 监听当前机器的所有ip,只写127.0.0.1只监听本地地址

2.3.2 路由视图

@app.route('/index',methods=['GET','POST''],endpoint='index')
def index():
    return "hello"

补充:

2.3.3 FlaskResponse

  1. render_template() 类似django的render(),帮助打开文件(HTML),认为文件内容是不安全的,显示出txt文件,这个函数里面有MarkupSafe,让浏览器认为标签是安全的.正常显示.
  2. redirect("/login") 重定向
  3. "" 直接返回字符串(django里面需要返回HttpResponse)
  4. jsonify #content-type:application/json 1.1.1 新特性,可直接返回dict类型,本质上就是在jsonify({'a':1}) ,而且只识别ASCII码. 用于API.
  5. send_file #自动打开并返回文件内容识别文件类型 content-type:文件类型

2.3.4 FlaskRequest

from flask import request  #公共变量  LocalProxy对象
request.form.get()  #django是这么写的request.POST.get(),而且flask没法拿文件,django的可以拿. -> .to_dit()
request.args.get()  #django的是request.GET.get() -> .to_dict()
request.files.get()  #获取文件,django获取方法在上文
request.method()  #获取method,类似django
request.headers
request.cookie
request.path
request.url
request.host
request.host_url

# 特别狠的角色
request.json  #请求头中带content-type:application/json
request.data  #content-type无法被识别 或者 没有Form

注意,
类名的话一般是驼峰体,ToDict.
方法的话一般是下划线,to_dict.

2.3.5 Session

from flask import session
app.secret_key = '@#$%^&*HVxghjk'

def vf():
    session["name"] = 1

存储在Cookies中
序列化 - {name:1} -> serect_key + 时间 + 签名 ->生成一个字符串->SESSION_COOKIE_NAME:"生成一个字符串"

反序列化 - SESSION_COOKIE_NAME:"生成一个字符串" ->secret_key + 时间 + 前面 >{name:1}

2.3.6 Flask配置

2.3.6.1 初始化 - app = Flask(name)

  1. template_folder
  2. static_folder
  3. static_url_path

2.3.6.2 Config Flask对象配置

Flask.default_config 获取默认配置
DEBUG
TESTING
SESSION_COOKIE_NAME
SECRET_KEY

settings.py

class DebugSetting(obj):
    DEBUG = True
    SECRET_KEY = 'adsfghjkhl!#$%@^'
    SESSION_COOKIE_NAME = 'i am not SESSION'

Flask.config.from_obj()
Flask.config['debug'] = True

2.3.7 蓝图

from flask import Bluepoint

不能被run的flask实例,没有config
蓝图作用 --- app隔离,URL管理

userBP = Blueprint("userBP",__name__,url__prefix="/user")
@userBP.route('/userBP')
def user_bp():
    return ""

思考:如何在蓝图中使用CBV结构.

2.3.8 特殊装饰器

  1. before_request #请求进入视图函数之前
  2. after_request # 结束视图函数之后,响应客户端之前

be+af实现中间件:
正常周期:be1 - be2 - vf - af2 - af1
异常周期:be1 - af2 - af1

  1. errorhandler(HTTP_ERROR_CODE 5xx 4xx) #重定义错误信息
  2. template_global 全局变量(def ab(a,b): return a+b,都可以用到jinja2里面了)
  3. route.

(template global要记得)

2.3.9 CBV

from flask import views

class Login(views.MethodView):
    methods = []
    
    def get(self):  #满足HTTP协议的8种请求
        pass
    
    def post(self):
        pass
Flask.add_url_rule(rule='/login',endpoint=None,view_func=Login.as_view(name="login"))

2.3.10 其他

三 面向对象补充

3.1 __doc__

class Foo:
    '我是描述信息'
    pass

print(Foo.__doc__)

它类的描述信息

注意,__doc__无法继承给子类.

3.2 __module__与__class__

3.2.1 概念

module 表示当前操作的对象在那个模块
class     表示当前操作的对象的类是什么

module:模块
python的module说白了就是Python文件,而python文件一般后缀为py,就是你的xxx.py而已.

MODULE的功能:
MODULE里面可以声明变量,经常用来声明程序中所需要的常量,或者用来存放全局变量。
MODULE里面可以定义自定义类型,再经过USE命令让程序中每一个函数都能使用这个类型。
MODULE里面可以编写函数,通常会把功能相关的函数放在同一个MODULE中。要是用这些函数,同样用use命令。
MODULE里面的函数,可以直接使用同一个。

补充:

关于module和library,前者一般在python说,后者在C,C#说.
关于module和package,前者是单个模块(一般是单个(偶尔是多个)python文件);后者是多个相关的module的组合,肯定是多个,相关的,Python文件的组合,是把相关的模块组织在一起,成为一个整体的.

3.2.2 实例

a.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = ‘SB'

lib/aa.py

lib/aa.py
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

index.py

3.4 __del__

__del__是析构方法,当对象在内存中被释放的时候,自动触发执行.

注意,若产生的对象仅仅是用户级的(python程序级别的),那么无需定义__del__,若,产生的对象同时像操作系统发起系统调用,也就是一个对象同时有用户级和内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用上了__del__.

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
# del f1
print('------->')

#输出结果
------->
执行我啦

典型应用场景:
创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放在内核空间内存中.
当程序结束后,python自动回收自己的内存空间,也就是用户态内存,而操作系统的资源没有被回收,这就需要我们定制__del__,在对象被删除前项操作系统发起数据库链接的系统调用,回收资源.

3.5 __enter__和__exit__

3.5.1 概念

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

3.5.2 实例1

class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name)

3.5.3 实例2

class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行

3.6 __call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

3.7 metaclass

元类是类的类,是类的模板元类是用来控制如何创建类的.
正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为.
元类的实例化的结果为我们用class定义的类,正如类的实例为对象.
(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

四 入门flask第三方包

4.1 Flask-Session

4.1.1 概念与原理重述

-------------------过度过度-------------------
在使用flask写应用程序的时候,我们会使用session来控制用户会话状态,但是我们无法确定session的保存位置,默认的flask保存session是模糊的,那我们可不可以控制session的存储位置呢,这就是flask-session:增加了服务器端支持会话到您的应用程序.

flask中session原理重述:
flask的session是基于cookie的会话保持。简单的原理即:

当客户端进行第一次请求时,客户端的HTTP request(cookie为空)到服务端,服务端创建session,视图函数根据form表单填写session,请求结束时,session内容填写入response的cookie中并返回给客户端,客户端的cookie中便保存了用户的数据。

当同一客户端再次请求时, 客户端的HTTP request中cookie已经携带数据,视图函数根据cookie中值做相应操作(如已经携带用户名和密码就可以直接登陆)。

在 flask 中使用 session 也很简单,只要使用 from flask import session 导入这个变量,在代码中就能直接通过读写它和 session 交互。

关于源码中最核心的三句话:

s = self.get_signing_serializer(app)
val = request.cookies.get(app.session_cookie_name)
data = s.loads(val, max_age=max_age)

return self.session_class(data)

注意,参数如下:
secret_key:密钥。这个是必须的,如果没有配置 secret_key 就直接使用 session 会报错
salt:为了增强安全性而设置一个 salt 字符串(可以自行搜索“安全加盐”了解对应的原理)
serializer:序列算法
signer_kwargs:其他参数,包括摘要/hash算法(默认是 sha1)和 签名算法(默认是 hmac)

flask内置session无法满足生产需求。因为将session数据全部保存在cookie中不安全且cookie存储数据量有限,但flask-session组件帮我们实现了将数据保存在服务器''数据库''中而只将sessionID保存在cookie中.
-------------------过度到Flask-Session咯........-------------------
重点啊鸭:
flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,该组件则将支持session保存到多个地方
例如:

redis:保存数据的一种工具,五大类型。
memcached
filesystem
mongodb
sqlalchmey:那数据存到数据库表里面

应用场景:
如果应用程序比较小,就用原生的加密cookie 保存session(内置);
如果应用程序比较大,就用redis(flask-session)

4.1.2 使用步骤

首先,要注意一件事情,使用了Session的话那就意味着放弃了自定义的secret_key,这个的话必然会报一个错误(未定义秘钥,实际上是没地方存储session),所以需要使用redis(实际上支持session保存到多个地方(以上5种都可以),这里我们选择redis).

4.1.2.1 安装Flask-Session

pip install flask-session
pip install redis

注意,pip要切换到pip所在的那个地方,或者把pip进行path环境变量的匹配.

app.config['SESSION_TYPE'] = 'redis'  #设置会话接口
app.config['SESSION_REDIS'] = Redis(host='192.168.16.18', port=6379, db=7)  #配置连接redis

上面两条命令非常重要!!!

redis下载完模块之后就可以导入了.
然后去官网下载软件,点击下载

安装完成后,无论是建立path环境变量然后再cmd启动redis-server.exe或者是直接运行redis-cli.exe都行.

redis的操作和Linux的一样.
redis最多可操作15条数据隔离的线路.
命令:
select 0~15: 选择任一数字会切换到该线路,select 0是还原(取消线路),在pycharm中进行构造的数据通过redis可查. (会相互影响)
set a1 b1 :建立a1:b1的键值对,(pycharm也可查)
get a1:查找键为a1的值.
更多redis命令猛击这里

4.1.2.2 使用

from flask import Flask, render_template, redirect, session
from flask_session import Session
from redis import Redis

app = Flask(__name__)

app.secret_key = 'dsaf'

app.config['SESSION_TYPE'] = 'redis'  #设置会话接口
app.config['SESSION_REDIS'] = Redis(host='192.168.16.18', port=6379, db=7)  #配置连接redis

# 测试pycharm和redis的连通性
a1 = Redis(host='192.168.16.18', port=6379, db=6)
a2 = Redis(host='192.168.16.18', port=6379, db=8)
a1.set('b1','123')
a2.set('b2','323')

print(a1.get('b1'))

# 以上是测试内容

Session(app) # 装载app到Session

@app.route('/index')
def index():
    session['k'] = 'v'
    return '123'

if __name__ == '__main__':
    app.run('0.0.0.0', 5000, debug=True)

注意,Session(app)的位置, 写错就会报错.

会话接口及对应的连接配置
一般情况下我们配置好SESSION_TYPE,然后把连接配置配置一下就可以。

  1. null:NullSessionInterface(默认)
  2. redis:RedisSessionInterface
    连接配置:SESSION_REDIS:一个redis.Redis例如,默认连接到 127.0.0.1:6379
  3. memcached:MemcachedSessionInterface
    连接配置:SESSION_MEMCACHED:一个memcache.Client实例,默认连接到127.0.0.1:11211
  4. filesystem:FileSystemSessionInterface
    连接配置:SESSION_FILE_DIR:存储会话文件的目录。默认使用当前工作目录下的flask_session目录。
    连接配置:SESSION_FILE_THRESHOLD:会话在开始删除之前存储的最大项目数,默认值为500
    连接配置:SESSION_FILE_MODE:存会话文件所需的文件模式,默认为0600
  5. mongodb:MongoDBSessionInterface
    连接配置:SESSION_MONGODB:一个pymongo.MongoClient实例,默认连接到127.0.0.1:27017
    连接配置:SESSION_MONGODB_COLLECT:您要使用的MongoDB集合,默认为“sessions”
  6. SQLAlchemy:SqlAlchemySessionInterface
    连接配置:SESSION_SQLALCHEMY:flask_sqlalchemy.SQLAlchemy实例,其数据库连接URI是使用配置SQLALCHEMY_DATABASE_URI参数
    连接配置:SESSION_SQLALCHEMY_TABLE:要使用的SQL表的名称,默认为“sessions”

参考:

4.1.3 错误

RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.

出现这种错误说明secret_key没写或者是Session初始化之后才发生的,写上或者调整顺序即可.

错误代码:

from flask import Flask,render_template,redirect,session
from flask_session import Session
from redis import Redis

app = Flask(__name__)
Session(app)
app.secret_key='dsaf'


app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='192.168.16.18',port=6379,db=7)


@app.route('/index')
def index():
    session['k'] = 'v'
    return '123'

if __name__ == '__main__':
    app.run('0.0.0.0',5000,debug=True)

正确代码:(应该把Session(app)放下面的,创建app这个flask类对象,配置完秘钥,配置完config,然后启动session就好了,就能把session存到你想存到的数据库了,此时我们也链接这这个数据库):

from flask import Flask,render_template,redirect,session
from flask_session import Session
from redis import Redis

app = Flask(__name__)

app.secret_key='dsaf'


app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='192.168.16.18',port=6379,db=7)

Session(app)
@app.route('/index')
def index():
    session['k'] = 'v'
    return '123'

if __name__ == '__main__':
    app.run('0.0.0.0',5000,debug=True)

4.1.5 其他

Flask-Session:session会话
Flask-CORS:跨域
Flask-WTF:相当于django的form表单
Flask-SQLAlchemy ORM:SQL操作

所有的flask第三方组件,都是需要app.config,不但要使用配置项,还可能会更改和添加配置项.

注意:
所有的redis不要和web服务器放在一起,redis的要放在内网服务器.redis有bug,无法修复,所以redis不要暴露在外网.!!!
select最高能到15.(同时隔离出15个数据),select 几就是几.

此外,
open_session  反序列化
save_session  序列化
上述两个操作有时间请练.

关于API的操作更适合使用CBV.

实例如下:(Session的配置项)
flask---Session的配置项(包括Session_redis)

4.1.6 最终代码

主程序运行

from flask import Flask, request, render_template, redirect, session
from flask_session import Session
from redis import Redis

# 导入写好的蓝图模块
from blueprint.viewer import login, index1

app = Flask(__name__)

# 进行配置
app.config['DEBUG'] = True
app.secret_key = 'asdfghjkjhrge'

# 设置会话接口
app.config['SESSION_TYPE'] = 'redis'

# 配置连接redis
app.config['SESSION_REDIS'] = Redis(host='192.168.16.18', port=6379, db=8)

# 类导入配置(注意使用app.default_config(如果忘记了的话))
from config_mode.settings import DebugSettings, TestSettings

app.config.from_object(DebugSettings)

# app.config.from_object(TestSettings)


# 装载app到Session
Session(app)


# 特殊装饰器
@app.before_request
def is_login():
    if request.path == '/login':
        return None
    if not session.get('uname'):
        return redirect('login')


# 报错信息(404的)
@app.errorhandler(404)
def error404(error_message):
    print(error_message)
    return redirect(
        'https://gitee.com/python_stack_20/teaching_plan/issues/IZ16D%E6%92%92%E6%89%93%E9%A3%9E%E6%9C%BA%E8%80%83%E6%A0%B8%E6%B3%95%E7%AC%AC%E4%B8%89%E6%96%B9%E7%9A%84%E8%90%A8%E5%87%A1%E7%BA%B3%E7%9A%84v')


app.register_blueprint(login)
app.register_blueprint(index1)

if __name__ == '__main__':
    app.run('0.0.0.0', 5000)

# 项目问题:暂时不会把404写到蓝图中,只会把蓝图中的化为CBV.

蓝图所在py文件(viewer.py)

from flask import Blueprint, request, redirect, session, render_template
from flask.views import MethodView
login = Blueprint('login1', __name__)

# 登录

class Login(MethodView):
    def get(self):
        return render_template('login.html')
    def post(self):
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index')
        else:
            return redirect('login')

login.add_url_rule('/login', endpoint='login1',view_func=Login.as_view('login'))

index1 = Blueprint('index', __name__)

# 主页

class Index(MethodView):
    def get(self):
        return render_template('index.html')

index1.add_url_rule('/index',endpoint='index',view_func=Index.as_view('index'))

标签:__,新篇章,flask,0.0,app,Flask,session
来源: https://www.cnblogs.com/smithpath/p/11178581.html