• flask框架初学-08-小知识


    使用flask-bootstrap

    前面我们使用了一些简单的html脚本样式进行编写html界面,实际情况下的界面往往更加丰富。在flask中,我们可以引入flask-bootstrap这个包,在代码中编写更加丰富的html

    步骤

    1、引入包并实例化Bootstrap()对象 exts–>init.py中

    from flask_bootstrap import Bootstrap
    
    bootstrap = Bootstrap()
    
    • 1
    • 2
    • 3

    2、apps/init.py中进行配置关联

    def create_app():
        app = Flask(__name__,template_folder='../templates',static_folder='../static')
        app.config.from_object(settings.DevelopmentConfig)
        # 初始化db
        db.init_app(app)
        bootstrap.init_app(app)
        return app
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、html中继承bootstrap下的base.html 及使用

    {% extends "bootstrap/base.html" %}
    
    • 1

    bootstrap下的base.html中内置的block:

    {% block title %} {% endblock %}
    
    {% block navbar %}  {% endblock %}
    
    {% block content %} {% endblock %}
    
    {% block styles %} {% endblock %}
    
    {% block scripts %} {% endblock %}
    
    {% block head %} {% endblock %}
    
    {% block body %} {% endblock %}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    密码加密

    注册

    # 加密密码
    pwdHash = generate_password_hash(password)
    
    • 1
    • 2

    登录

    # 判断密码是否一致  结果---->bool:False True
    check_password_hash(pwdHash,password)  
    
    • 1
    • 2

    会话机制

    会话机制是用来跟踪用户的整个会话,可以在整个会话中保持用户的信息,常用的会话跟踪技术是Cookie和Session,常用于存储用户的登录信息。如登录某个网站,只要会话不结束,在网站中不需要每次重新登录才能展示相关信息以及可执行相关操作

    cookie方式

    保存:
            通过response对象保存
            response = redirect(XXX)
            response = render_template(XXX)
            response = Response()
            response = make_response()
            response = jsonify()
            通过对象调用方法
            response.set_cookie(key,value,max_age)
     获取:
            通过request对象获取:
            request.form.get()
            request.args.get()
            cookie也在request对象中
            request.cookies.get(key) ----->value
    
     删除:
            通过response对象删除,将浏览器中存储的cookie对应的key删除:
            response = redirect(XXX)
            response = render_template(XXX)
            response = Response()
            response = make_response()
            response = jsonify()
            通过对象调用方法
            response.delete_cookie(key)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    session方式(是在服务器端存储用户ip信息)

    注意:必须要在配置文件中设置: SECRET_KEY=“xxxxx”,添加SECRET_KEY的目的是为了sessionid的加密,不设置会报错

    保存:
         session['uid'] = user.id
    获取:
         uid = session.get('uid')
    删除:
         del session['uid']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    钩子函数

    在程序中,针对一些经常需要被调用的函数,又不想在代码中进行重复编码的情况,或者实现一些只需要在程序每次重新启动时调用函数的情况下,可以走center路由选择钩子函数。比如对登录权限的验证:走center路由,判断用户是否是登录状态,如果用户登录了,可以正常显示页面,如果用户没有登录,则自动跳转到登录页面进行登录,登录后才可以进行查看

    钩子函数主要分为两大类:直接应用在APP上的、应用到蓝图上的

    应用在APP上

    before_first_request  ---  第一次请求时运行
    before_request  --- 每次请求时运行
    after_request   --- 每次请求运行后执行
    teardown_request  --- 最后一次请求时运行
    
    • 1
    • 2
    • 3
    • 4

    应用到蓝图上

    before_app_first_request   --- 在app启动时只使用一次
    before_app_request   --- 每次调用蓝图都会使用
    after_app_request   --- 在每次蓝图调用完,返回response的时候调用
    teardown_app_request --- app运行完之后调用一次
    
    • 1
    • 2
    • 3
    • 4

    example

    # 要求登录后才能访问的url
    required_login_list = ['/users/center',
                           '/users/change',
                           '/users/publish',
                           '/users/upload_photo',
                           '/users/photo_del',
                           '/articles/add_comment',
                           '/users/aboutme',
                           '/users/showabout']
    # 在app启动时只使用一次
    @users_bp.before_app_first_request
    def first_request():
        print('before_app_first_request')
        if request.path in required_login_list:
            print("需要验证用户的登录情况!")
    
    # 每次调用蓝图都会使用
    @users_bp.before_app_request
    def before_request1():
        print('before_app_request',request.path)
        if request.path in required_login_list:
            id = session.get('uid')
            print('------>',id)
            if not id:
                return render_template('user/login.html')
            else:
                user = User.query.get(id)
                # g对象 本次请求的对象
                g.user = user
                print(g.user.username)
    
    # 在蓝图调用完,返回response的时候调用
    @users_bp.after_app_request
    def after_request_test(response):
        print('after_app_request')
        response.set_cookie('a','bbbb',max_age=19)
        return response
    
    # app运行完之后调用一次
    @users_bp.teardown_app_request
    def teardown_request_test(response):
        print('teardown_app_request')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    文件上传

    A.上传本地

    表单:

    <form action="提交的路径" method="post" enctype="multipart/form-data" class="form-inline ">
      <input type="file" name="photo" class="from-control">
      <input type="submit" value="上传相册" class="btn btn-default">
    form>
    
    • 1
    • 2
    • 3
    • 4

    view视图:

    # photo是FileStorage
    photo = request.files.get('photo')    
    # 属性和方法:FileStorage---》fs
    fs.filename
    # path上传的路径:os.path.join(UPLOAD_PIR,filename)
    fs.save(path)  
    # 将上传的内容改成二进制方式
    fs.read()    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    B.上传到云端 (对象存储)

    本地资源优先或者是空间有限 https://developer.qiniu.com/kodo/1242/python ------> 参照python sdk

    Example:

    user/view.py 上传照片

    @users_bp.route('/upload_photo',methods=['GET','POST'])
    def upload_photo():
        # 获取上传的内容
        photo = request.files.get('photo')
        ret,info = upload_qiniu(photo)
        if info.status_code == 200:
            photo = Photo()
            photo.photo_name = ret['key']
            photo.user_id = g.user.id
            db.session.add(photo)
            db.session.commit()
            return '上传成功!'
        else:
            return '上传失败!'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    util.py 使用qinniu存储

    # 上传本地文件
    def upload_qiniu(filestorage):
        # 初始化Auth状态
        q = Auth(access_key, secret_key)
        # 初始化BucketManager
        bucket = BucketManager(q)
        # 获取文件名
        filename = filestorage.filename
        # 重新拼接成一个不重复的文件名
        ran = random.randint(1,1000)
        suffix = filename.rsplit('.')[-1]
        key = filename.rsplit('.')[0]+ '_' +str(ran) + '.' +suffix
    
        #生成上传 Token,可以指定过期时间等
        token = q.upload_token(bucket_name, key, 3600)
        #要上传文件的本地路径
        # localfile = os.path.join(Config.UPLOAD_ICON_DIR,phone_name)
        ret, info = put_file(token, key, filestorage.read(), version='v2')
        # print(info)
        assert ret['key'] == key
        assert ret['hash'] == etag(filestorage.read())
        return ret,info
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    分页

    # 返回所有记录
    print(pagination.items) 
    # 返回当前的页码数
    print(pagination.page)  
    # 返回当前页的前一个页码数
    print(pagination.prev_num)  
    # 返回当前页的后一个页码数
    print(pagination.next_num)  
    # 判断是否还有下一页
    print(pagination.has_next) 
    # 判断是否有上一页
    print(pagination.has_prev) 
    # 总共有几页
    print(pagination.pages)   
    # 总的记录条数
    print(pagination.total)   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    日志

    可参考另一篇文章:https://blog.csdn.net/weixin_42724501/article/details/121012689

    1、使用app自带 默认输出到终端控制台

    app.logger.info('这是一个info测试')
    app.logger.debug('这是一个debug测试')
    app.logger.warning('这是一个warning测试')
    app.logger.error('这个是一个error测试')
    
    • 1
    • 2
    • 3
    • 4

    2、通过logging进行创建

    import logging
    
    # 默认flask的名字叫:app
    logger = logging.getLogger('name')    
    logger = logging.getLogger('app')
    # 保存到文件
    logging.basicConfig(filename='log.txt',filemode='a',level=logging.WARNING,
                        format='%(asctime)s - %(name)s - %(levelname)s -%(message)s')
    '''
    方法二:
    logger.setLevel(level=logging.WARNING)
    handler = logging.FileHandler("log2.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    
    方法三:
    使用logger.info('message')
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    @users_bp.route('/logger')
    def logger_test():
        logger.warning("首页的警告!!!!!!")
        # 需要打印到日志文件中,则:logging.getLogger('app')中必须是app
        app.logger.warning('首页警告2!!!!!')
        return render_template('')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Redis

    redis的详细使用请查看官网,这里只简单地介绍在flask中的使用

    1、导入相关package

    pip install redis
    pip install flask-caching
    
    • 1
    • 2

    2、exts->init.py配置

    cache = Cache()
    
    • 1

    3、init.py中初始化:

    config = {
            'CACH_TYPE': 'REDIS',
            'CACHE_REDIS_HOST': '127.0.0.1',
            'CACHE_REDIS_PORT': 6379
            # 'CACHE_REDIS_PASSWORD':'',
            # 'CACHE_REDIS_DB':''
        }
    def create_app():
        cache.init_app(app = app,config = config)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、使用

    视图函数缓存

    @users_bp.route('/')
    @cache.cached(timeout=50)  #过期时间
    def index():
        pass
    
    • 1
    • 2
    • 3
    • 4

    缓存键值对

    • 设置
    cache.set(key,value,timeout=second)
    cache.set_many([(key,value),(key,value),...])
    
    • 1
    • 2
    • 获取
    cache.get(key)
    cache.get_many(key1,key2,...)
    
    • 1
    • 2
    • 删除
    cache.delete(key)
    cache_delete_many(key1,key2,...)
    cache.clear()
    
    • 1
    • 2
    • 3

    redis基本语法

    五种数据类型:string、hash、list、set、zset(sorted:有序集合)

    string: SET [key] [value]       设置key-value
                GET [key]               获取key相对应的value值
    hash:   一个键值(key-value)对集合  一个string类型的field和value的映射表 适用于存储对象
                HMSET [key] field1 [value1] field2 [value2]  设置hash列表值
                HGET [KEY] field1                            获取hash列表值
    
    通用命令:
             EXISTS [KEY]        检查指定key是否存在
             DEL [KEY]           删除hash键值
             keys pattern        查找所有符合给定模式的key
             MOVE key db         将当前数据库的key移动到给定的数据库db当中
             PERSIST key         移除key的国企时间,key将持久保持
             PTTL key            以毫秒为单位返回key的剩余过期时间
             EXPIRE key [seconds]       为给定的key设置过期时间,以秒计
             EXPIREAT key [timestamp]    设置过期时间,时间戳
             PEXPIRE key [milliseconds]  设置过期时间,以毫秒计
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    wtform

    用于对提交的表单数据做格式验证,flask-wtf集成了wtform, csrf的保护和文件上传功能,以及图形验证码

    使用

    1、安装

    pip install Flask-WTF
    
    # 全局使用csrf保护
    csrf = CSRFProtect(app=app)
    
    # 必须需要设置SECRET_KEY这个配置项
    app.conf['SECRET_KEY'] = 'fgfhdgfhh32'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、定义form.py

    # 在文件中添加
    class UseForm(FlaskForm):
        name = StringField('name',validators=[DataRequired()])
    
    • 1
    • 2
    • 3

    各种Field类型

    类型
    字符串StringField
    密码PasswordField
    整型IntegerField
    小数DecimalField
    浮点FloatField
    布尔BooleanField
    单选RedioField
    多选SelectField
    日期DatetimeField

    各种验证

    validators
    数据校验DataRequired
    匹配校验EqualTo
    IP校验IPAddress
    长度校验Length
    数据范围校验NumberRange
    URL校验URL
    邮件格式校验Email
    正则校验Regexp

    3、html中设置

    <form action="" method="post">
            {{ uform.csrf_token }}
            <p>{{ uform.name }}{% if uform.name.errors %}{{ uform.name.errors }}{% endif %}p>
            <p>{{ uform.password }}{% if uform.password.errors %}{{ uform.password.errors }}{% endif %}p>
            <p><input type="submit" value="提交"> p>
    form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4、views中提交验证

    @users_bp.route('/testForm',methods=['GET','POST'])
    def testForm():
        uform = UserForm()
        if uform.validate_on_submit():
            print(uform.name)
            print(uform.password)
            return '提交成功!'
        return render_template('formtest.html',uform)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5、文件上传

    # 上传使用的是FileField,如果需要指定上传文件的类型需要使用:FileAllowed
    class UserForm(FlaskForm):
        icon = FileField(label="用户头像",Validators=[FileRequired(),FileAllowed(['jpg','png','gift'],message="必须是图片文件格式")])
    
    • 1
    • 2
    • 3

    模板中的使用同其他类型的字段,但是必须在form上面

    enctype="multipart/form-data"
    
    • 1

    视图函数中如果验证成功,通过

    # icon是FileStorage类型
    icon = uform.icon.data    
    filename = secure_filename(icon.filename)
    icon.save(os.path.join(UPLOAD_DIR,filename))
    
    • 1
    • 2
    • 3
    • 4

    6、验证码(依赖谷歌传送验证码)

    必须在setting.py配置

    # secret_key加密码
    SECRET_KEY = 'JIAMIMA'
    RECAPTCHA_PUBLIC_KEY = "SHAJDGBCYSDCUDUSAD"
    RECAPTCHA_PRIVATE_KEY = "AUERHDNSBDSDSADSLADDQ"
    RECAPTCHA_PARAMETERS = {'hl':'zh','render':'explicit'}
    RECAPTCHA_DATA_ATTRS = {'theme':'dark'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    form.py中

    class UserForm(FlaskForm):
        recaptcha = RecaptchaField(label="验证码")
    
    • 1
    • 2

    7、验证码(自己绘制验证码)

    pip install pillow
    
    • 1

    form.py

    class UserForm(FlaskForm):
        recaptchabyself = StringField(label='验证码')
    
    def validate_recaptchabyself(self,data):
        input_code = data.data
        code = session.get('valid')
        if input_code.lower() != code.lower():
            raise ValidationError("验证码错误!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    工具类

    # 获取随机颜色
    def get_random_color(length):
        return (random.randint(0,255),random.ranint(0,255),random.randint(0,255))
    # 生成图片
    def generate_image(length):
        s = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRTSUVWXYZ1234567890'
        size = (130,50)
        # 创建画布
        im = Image.new('RGB',size,color=get_random_color())
        # 创建字体
        ImageFont.truetype('font/segoerp.ttf',size=35)
        # 创建ImageDraw对象
        draw = ImageDraw.Draw(im)
        code = ''
        # 绘制验证码
        for i in random(length):
            c = random.choice(s)
            code += c
            # draw.text(坐标位置,绘制谁,用什么颜色,字体)
            draw.text((5+ random.randint(4,7) + 25 * i, random.randint(1,4)),
                      text=c,fill=get_random_color(),
                      font=font)
            # 绘制干扰线
            for i in range(8):
                x1 = random.randomint(0,130)
                y1 = random.randomint(0,50/2)
                x2 = random.randomint(0, 130)
                y2 = random.randomint(50/2, 130)
                draw.line(((x1,y1),(x2,y2)),fill=get_random_color())
        im = im.filter(ImageFilter.EDGE_ENHANCE)
        return im, code
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    视图

    @users_bp.route('/testForm',methods=['GET','POST'])
    def testForm():
        uform = UserForm()
        if uform.validate_on_submit():
            print(uform.name)
            print(uform.password)
            name = uform.name.data
            password = uform.password.data
            phone = uform.phone.data
            icon = uform.icon.data
            filename = secure_filename(icon.filename)
            BASE_DIR = os.path.dirname(os.path.abspath(__file__))
            STATIC_DIR = os.path.join(BASE_DIR,'static')
            UPLOAD_DIR = os.path.join(STATIC_DIR,'upload')
            icon.save(os.path.join(UPLOAD_DIR,filename))
            return '提交成功!'
        return render_template('formtest.html',uform)
    
    @users_bp.route('/image')
    def get_image():
       im,code = generate_image()
       # 将image对象转成二进制
       buffer = BytesIO()
       im.save(buffer,"JPEG")
       buf_bytes = buffer.getvalue()
       # 保存到session中
       session['valid'] = code
       response = make_response(buf_bytes)
       response.headers['Content-Type'] = 'image/jpg'
       return response
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    html

    <p>{{ uform.recaptchabyself.label }}:{{ uform.recaptchabyself }}<img src="{{ url_for('users.get_image') }}" alt="" id="img">
    <p>{% if uform.recaptcha.errors %}{{ uform.recaptcha.errors.0 }}{% endif %}p>
    
    • 1
    • 2

    8、消息闪现

    a、在一个请求结束的时候添加flash

    flash('登录成功!','info')
    flash(username,'warning')
    flash('hahha', 'error')
    
    • 1
    • 2
    • 3

    b、在当前请求中渲染获取或者仅仅下一个请求中可以获取

    获取闪现内容:
    1、 with_categories=True为获取多个消息(元组)

     get_flashed_messages(with_categories=[True/False])
    
    • 1

    ​ 2、 有针对性地获取可闪现消息

    get_flashed_messages(category_filter=["error"])
    
    • 1
  • 相关阅读:
    NSSCTF做题第9页(3)
    java进阶1——JVM
    pcl--第十一节 点云外接立方体和点云模板匹配
    1043 输出PATest
    ssm+vue的养老院老人健康监护平台(有报告)。Javaee项目,ssm vue前后端分离项目。
    centos7.6配置用户免密及权限
    linux部署kafka并集成springboot
    适用于 Linux 的 WPF:Avalonia
    【35】gorm gen tool生成数据库对应go文件
    使用create-react-app脚手架创建react项目
  • 原文地址:https://blog.csdn.net/weixin_42724501/article/details/126232397