Flask项目搭建 2


前言

提示:这里可以添加本文要记录的大概内容:


一、目录框架

1.settings目录

用来存放配置信息
一般有秘钥和SQLAlchemy的配置信息
例如定义两个py文件 index dev分别存储

index.py

class Defaut():
    SECRET_KEY = 'hweyuwqq53461jeihjebjd34jf3'

dev.py

继承前边的配置类 接着配置数据库

from settibgs.index import Defaut

class Dev(Defaut):
    SQLALCHEMY_DATABASE_URI = 'mysql://root:password@127.0.0.1:3306/sjk527'
    SQLALCHEMY_ECHO = False
    SQLALCHEMY_TRACK_MODIFICATIONS = True

2.create_flask.py

用来定义工厂函数 生成应用对象app 获取配置信息
注册蓝图 初始化项目 钩子方法 配置跨域

from flask import Flask
from flask_cors import CORS
from common.utils.jwt_auth import jwt_authentication
from models import db
from views.user import user_bp

def create_flask_app(info):
    # 创建应用app
    app = Flask(__name__)
    # 从对象获取配置信息
    app.config.from_object(info)
    # 注册蓝图 
    app.register_blueprint(user_bp)
    # 初始化整个项目
    db.init_app(app)
    # 配置跨域
    CORS(app)
    # 使用钩子方法 验证token
    app.before_request(jwt_authentication)
    return app

3.main.py

启动项目的主入口
需要导入前边的配置文件和工厂函数

from create_flask import create_flask_app
from settibgs.dev import Dev

app = create_flask_app(Dev)

if __name__ == '__main__':
    app.run()

4.models目录!

用来存放模型类
需要创建一个__init__.py来创建一个SQLAlchemy对象
一般命名为 db

init.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

users.py

这里用用户和角色来示例 一对多关系

# 一对多
    class User(db.Model):
        __tablename__ = 'tb_user'
        uid = db.Column(db.Integer, doc='用户id')
        name = db.Column(db.String(33), doc='用户名')
        phone = db.Column(db.String(11), doc='手机号')
        role_id = db.Column(db.Integer, db.ForeignKey('tb_role.rid'), doc='角色外键')

    class Role(db.Model):
        __tablename__ = 'tb_role'
        rid = db.Column(db.Integer, doc='角色id')
        name = db.Column(db.String(33), doc='角色名')

        r_user = db.relationship('User', backref='roles')

6.migrate.py 迁移脚本

生成Manager对象
迁移
添加迁移命令
迁移主入口
启动Manager
迁移脚本一定要导入所有的模型, 不然不会生成需要的表

from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager

from main import app
from models import db
from models.user import *

manage = Manager(app)
migrate = Migrate(app, db) # 此处db代表迁移所有表
manage.add_command('db', MigrateCommand) # 'db'迁移关键字

if __name__ == '__main__':
    manage.run()

7.model_fields 目录

用来存放需要序列化的字段
需要导入fields
搭配marshal使用
例如:以下整型 字符串 时间 布尔类型

from flask_restful import fields
# 文章字段
news_fields = {
    'nid': fields.Integer,
    'title': fields.String,
    'ctime': fields.DateTime,
    'is_follow':fields.Boolean,
}

8 views视图目录

用来存放需要的视图文件
一般使用蓝图
搭配falsk_restful
例如: 发送短信验证码视图

此处曾报错

1)不同蓝图前url不能相同 url_prefix='不能相同'

import json
import random
import redis
from flask import Blueprint, jsonify
from flask_restful import Api, Resource, reqparse
from werkzeug.security import generate_password_hash
from common.celery.tasks import sendSmsCode
from common.utils.jwtToken_generate_check import generate_token
from models.user import *

# 生成蓝图 
user_bp = Blueprint('user_bp', __name__, url_prefix='/v1_0')
# 生成api对象
api = Api(user_bp)

# 发送短信验证码  配置容联云 celery异步 生成 发送
class SendSmsCode(Resource):  # 继承Resource

    def post(self):
        smsCode = f'{random.randint(100000, 999999)}'
        print('生成的短信验证码是====={', smsCode)
        parser = reqparse.RequestParser()
        parser.add_argument('mobile')
        args = parser.parse_args()
        mobile = args['mobile']
        print('前端传来的手机号', mobile)
        #
        ret = sendSmsCode.delay(mobile, smsCode)
        if ret:   # 发送成功  存入redis
            rds = redis.Redis(db=2)
            key = f'mobile:{mobile}:smsCode'
            rds.set(key, smsCode, ex=2*60*60)
            rds.close()
            return jsonify(msg='手机号发送成功, 注意查收', code=200)
        else:
            return jsonify(msg='手机号发送失败', code=500)

# 发送短信验证码
api.add_resource(SendSmsCode, '/sms_code')


9 common目录

通用目录 目前存放 :
celery 异步发短信 #
生成token 校验token #
鉴权钩子 #
强制登录 #
定时任务
七牛云
缓存

此处曾报错

1 容联云发送短信验证码
1)@app.task 在函数上方使用它才能异步delay
2) 开启celery异步的命令 :文件目录tasks
celery -A 要执行的任务所在文件目录 worker -l info -P eventlet
2 生成tokrn
1)生成token需要的current_app 两种调用方式
secret = current_app.config.get(‘SECRET_KEY’)
secret = current_app.config[‘SECRET_KEY’]

3 强制登录

1)`@wraps(func) `
强制登录 定义装饰器
def login_qlvi(func):
    @wraps(func)
    def wrap(*args, **kwargs):
        if g.user_id:
            return func(*args, **kwargs)
        else:
            return {'msg': '请先登录'}, 401
    return wrap

生成token 校验token

生成token 与 校验token 注意s的区别
algorithmalgorithms

import datetime
import jwt
from flask import current_app

# TODO 报错记录 应用上下文获取配置数据 current_app.config['名称']
# 封装生成token
def generate_token(payload, exprie):
    secret = current_app.config.get('SECRET_KEY')
    payload.update({'exp': exprie})
    token = jwt.encode(payload, secret, algorithm='HS256')
    print('这是第一次生成的token', token)
    return token

# 校验token
def check_token(token):
    secret = current_app.config.get('SECRET_KEY')
    try:
        payload = jwt.decode(token, secret, algorithms='HS256')
        return payload
    except:
        return None


# 生成两个token
def generate_token2(payload, is_refresh=True):
    exprie = datetime.datetime.now() + datetime.timedelta(hours=24)
    token = generate_token(payload, exprie)
    if is_refresh:
        exprie_ = datetime.datetime.now() + datetime.timedelta(days=15)
        payload.update({'is_refresh': True})
        refresh_token = generate_token(payload, exprie_)
    else:
        refresh_token = None

    return token, refresh_token

鉴权钩子

from flask import g, request
from common.utils.jwtToken_generate_check import check_token

def token_authcxx():
    print('########进入鉴权钩子########')
    token = request.headers.get('Authorization')
    print('鉴权里是否获取前端token:===?', token)
    payload = check_token(token)
    if payload:
        g.user_id = payload['user_id']
    else:    # 有载荷就赋值g对象 没有就放过
        pass

celery异步发短信

celery_main.py 配置celery

from celery import Celery

app = Celery('myWorker', broker='redis://127.0.0.1:6379/5',
             backend='redis://127.0.0.1:6379/6')

app.autodiscover_tasks(['common.celery.tasks'])

tasks.py 任务文件

import json
from ronglian_sms_sdk import SmsSDK
from common.celery.celery_main import app

accId = '8a216da8804bxa8a5018064a39933069b'
accToken = '09880a5e3fc94046b3cc1x47e240ad169'
appId = '8a216da8x804ba8a5018064a39a3706a2'

@app.task  # 此处如果不写就不能delay异步发送
def sendSmsCode(mobile1, smsCode, expire=60):

    sdk = SmsSDK(accId, accToken, appId)
    tid = '1'
    mobile = mobile1
    datas = (f'{smsCode}', f'{expire}')

    res = sdk.sendMessage(tid, mobile, datas)
    print('res======>?', res)
    # return res
    if res:  # 只要有响应就通过 可以不写真实手机号
        return True
    else:
        return False
        
    # 这个判断则是 必须发送成功短信才能返回Ture
    # if json.loads(res)['status Code'] == "000000":
    #     return True
    # else:
    #     return False

生成七牛云token

from qiniu import Auth, put_file, etag
import qiniu.config
#需要填写你的 Access Key 和 Secret Key
access_key = '74F2GCXwNJ2c9fgiD3ufHUUDXUrSdgNXW6kmIrXY'
secret_key = 'LqiDv9FZT8oLhDMHrLWwR-HP1vOoEguyQWUQlmNz'
def qiniu_token():    
    q = Auth(access_key, secret_key)  # 构建鉴权对象
    bucket_name = 'p7lotk'  # 要上传的空间
    #生成上传 Token,可以指定过期时间等
    token = q.upload_token(bucket_name)
    return token

视图中调用

class QiniuToken(Resource):
    @login_required
    def get(self):
        """生成七牛云上传的token"""
        token = qiniu_token()
        print('七牛云的token======', token)
        return jsonify(msg='七牛云生成的token', code=200, data={'token': token})

缓存

用户缓存
获取缓存 清除缓存 判断是否在缓存中

import json
import random
import redis
from flask import jsonify
from redis import RedisError
from sqlalchemy.orm import load_only

from models.users import UserModel



# 有效期设置:
class BaseCacheTTL(object):

    TTL = 0  # 由子类设置
    MAX_DELTA = 10 * 60  # 随机的增量上限

    @classmethod
    def get_val(cls):
        return cls.TTL + random.randrange(0, cls.MAX_DELTA)


# 用户资料数据缓存时间
class UserProfileCacheTTL(BaseCacheTTL):
    TTL = 30 * 60


# key不存在时的有效期
class UserNotExistsCacheTTL(BaseCacheTTL):
    TTL = 1*60


# 实现 缓存
class UserCacha():
    def __init__(self, user_id):
        self.rds = redis.Redis()
        self.user_id = user_id
        self.key = f'user:{user_id}:profile'

    # 获取缓存
    def get(self):
        """
        获取缓存
        :return: 缓存
        获取缓存 --- 存在 返回缓存
                ---不存在 查找mysql
                            ==查找到 存入缓存 并返回查询数据
                            ==未找到 不存在 设置Redis中记录的值为-1
        """
        # rds = redis.Redis()
        user_rds = self.rds.get(self.key)
        if user_rds:   # 存在 返回缓存
            user_rds1 = json.loads(user_rds)
            return user_rds1
        else:          # 不存在 查找mysql
            try:     # options(load_only(模型名.字段名)) 惰性查询 需要时才会执行查询
                user = UserModel.query.filter_by(uid=self.user_id).first()
                print('查询数据库用户数据:', user)
            except Exception as e:
                print(e)
                return jsonify(msg='数据库查询操作失败', code=500)

            # 查找到空数据, redis记录为-1
            if user is None:
                self.rds.setex(self.key, UserNotExistsCacheTTL.get_val(), -1)
                # user_rds = rds.get(self.key)
                return None

            # 查找到具体数据 返回需要缓存的数据
            else:
                user_dict = {
                    'uname': user.uname,
                    'mobile': user.mobile,
                    'account': user.account,
                    'gender': user.gender,
                    'profile_photo': user.profile_photo,
                    'is_media': user.is_media,
                    'introduction': user.introduction,
                    'certificate': user.certificate,
                    'article_count': user.article_count,
                    'following_count': user.following_count,
                    'fans_count': user.fans_count,
                    'like_count': user.like_count
                }
                # 存入redis
                self.rds.set(self.key, json.dumps(user_dict), ex=UserProfileCacheTTL.get_val())
                return user_dict

    # 清除缓存
    def clear(self):
        # rds = redis.Redis()
        try:
            self.rds.delete(self.key)
        except RedisError as e:
            print(f'删除缓存的报错信息是{e}')

    # 判断用户缓存是否实现
    def user_exists(self):
        # 查询redis  1存在      -1False  否则返回Ture
        #           不存在   查询数据库 查询到 存入 返回Ture   查不到 存-1 返回 Flase
        try:
            user_rds = self.rds.get(self.key)
        except Exception as e:
            print(f'查询redis数据报错{e}')
            user_rds = None
        if user_rds:
            if user_rds == b'-1':
                return False
            else:
                return True
        else:
            user = UserModel.query.filter_by(uid=self.user_id).first()
            # 查找到空数据, redis记录为-1
            if user is None:
                self.rds.setex(self.key, UserNotExistsCacheTTL.get_val(), -1)
                # user_rds = rds.get(self.key)
                return None
            else:
                user_dict = {
                    'uname': user.uname,
                    'mobile': user.mobile,
                    'account': user.account,
                    'gender': user.gender,
                    'profile_photo': user.profile_photo,
                    'is_media': user.is_media,
                    'introduction': user.introduction,
                    'certificate': user.certificate,
                    'article_count': user.article_count,
                    'following_count': user.following_count,
                    'fans_count': user.fans_count,
                    'like_count': user.like_count
                }
                self.rds.setex(self.key, UserProfileCacheTTL.get_val(), json.dumps(user_dict))
                return user_dict

缓存在视图中的使用

class UserInfo(Resource):
    """从缓存中获取用户信息
    封装的缓存类 UserCacha() 使用时需要传参 id   
    .函数名() 调用对应的方法
    """
    def get(self, id):
        # user = UserModel.query.filter_by(uid=id).first()
        # UserCacha(id).clear()   # 这里调用清除缓存
        
        user = UserCacha(id).get()  # 这里调用获取缓存
        if user:
            user11 = marshal(user, user_fields)
            return user11

后续报错总结

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值