装饰器与Celery工具

14 篇文章 9 订阅

1. 登录装饰器

Flask

需要自定义 登录装饰器

def admin_log_req(f):
    """
    登录装饰器
    """

    @wraps(f)
    def decorated_function(*args, **kwargs):
        if "admin" not in session:
            return redirect(url_for("admin.login", next=request.url))
        return f(*args, **kwargs)

    return decorated_function

functools.wraps 的作用 让wrapper继承所有func(视图函数)传递的属性
在这里插入图片描述

from functools import wraps

from flask import g, make_response, session
from flask.json import jsonify
from flask_wtf import csrf
from werkzeug.routing import BaseConverter

def login_required(view):
    """
    登录状态检查装饰器,并保存用户状态
    :param view: 视图函数
    :return 未登录: 返回session错误; 已登录: 返回被装饰的视图函数
    """
    @wraps(view)
    def view_wrapper(*args, **kwargs):
        from ihome.models import User
        from ihome.utils.response_code import RET

        user_id = session.get('user_id')
        if user_id is None:
            return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')
        try:
            user = User.query.get(user_id)
        except Exception as e:
            return jsonify(errno=RET.DBERR, errmsg='读取用户信息异常')
        else:
            g.user_id = user.id
            g.name = user.name
            g.mobile = user.mobile
        return view(*args, **kwargs)

    return view_wrapper

Django

Django 中有自带的登录装饰器 login_required与LoginRequiredMixin
在这里插入图片描述
老版的Django使用装饰器login_required来限制用户登录
新版的Dajngo通过继承LoginRequiredMixin类来限制用户登录,必须是第一个继承,在继承列表最左侧位置

utils.mixin.py

from django.contrib.auth.decorators import login_required
#  只有在使用了内置的登录信息认证系统的时候,才能使用内置的装饰器函数login_required()

class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **initkwargs):
        # 调用父类的as_view,以下两种方法都可调用super的方法
        # view = super().as_view(**initkwargs)
        view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
        return login_required(view)
from utils.mixin import LoginRequiredMixin  # 自定义认证检查
class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):

2. 权限控制装饰器

def admin_auth(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        admin = Admin.query.join(
            Role
        ).filter(
            Role.id == Admin.role_id,
            Admin.id == session["admin_id"]
        ).first()
        # print(admin.role)
        if admin.role.name != '超级管理员':
            auths = admin.role.auths  # 登录用户的权限操作id
            auths = list(map(lambda v: int(v), auths.split(",")))
            auth_list = Auth.query.all()  # 所有权限
            urls = [v.url for v in auth_list for val in auths if val == v.id]  # 筛选出所有路由
            rule = request.url_rule
            # print(rule)
            if str(rule) not in urls:
                abort(404)
            return f(*args, **kwargs)
        else:
            return f(*args, **kwargs)

    return decorated_function

花费时间装饰器

作用:程序花费时间

def cost(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} cost {end-start}s ')
        return res
    return wrapper

修改文件名

def change_filename(filename):
    """
    修改上传文件名称
    """
    # print("原始文件name", filename)
    fileinfo = os.path.splitext(filename)
    # print("分割后:", fileinfo)
    filename = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + str(uuid.uuid4().hex) + list(fileinfo)[-1]
    # print("最终文件name", filename)
    return filename

自定义re转换器

from werkzeug.routing import BaseConverter
class ReConverter(BaseConverter):
    """ 自定义re转换器 """

    def __init__(self, url_map, regex):
        super().__init__(url_map)
        # 保存正则表达式
        self.regex = regex

@api.route('/sms_code/<re(r"1[345789]\d{9}"):mobile_num>')
def get_sms_code(mobile_num):

alilog

init.py

# -*- coding: utf8 -*-

from enum import Enum


class LogLevelEnum(Enum):
    """
    log等级
    """
    # 记录子系统初始化,核心请求正常执行,用于查询更高级别日志的上下文
    INFO = 3
    # 记录系统可能出现的问题,例如网络波动,硬盘空间不足等,不需要立即处理,但要及时维护
    WARN = 4
    # 影响个别用户访问的问题,需要立即处理
    ERROR = 5


from .alilog_client import alilog
from .logger import ali_log_exception

__all__ = [LogLevelEnum, alilog, ali_log_exception]

/log/logger.py

# -*- coding: utf8 -*-

import traceback
from functools import wraps

from .alilog_client import alilog


def ali_log_exception(func):
    """
    a decorator that wraps the passed in function and logs exceptions using ali log
    :param func:
    :return:
    """

    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            err = f"""There was an exception in {func.__name__}, 
            Traceback: {traceback.format_exc()}"""
            alilog.log_warn(err)

    return wrapper

/log/alilog_client.py

# -*- coding: utf8 -*-

import os
import time
import traceback
from functools import wraps

from aliyun.log.logitem import LogItem
from aliyun.log.logclient import LogClient
from aliyun.log.putlogsrequest import PutLogsRequest

from config import config
from . import LogLevelEnum
from ..utils import PassObject


def program_exception(func):
    """
    a decorator that wraps the passed in function and send warning email to developers
    in case that ali log doesn't work
    :param func:
    :return:
    """

    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            # send email
            subject = "阿里云日志宕机"
            content = f"""{func.__name__} 出错
            Traceback: {traceback.format_exc()}"""
            recipients = ["hongfu.pan@stardust.ai", "xin.zhang@stardust.ai"]
            # send_email(subject, content, recipients)

    return wrapper


class AliLog:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)

        return cls._instance

    def __init__(self, endpoint: str, access_key_id: str, access_key: str, project: str, log_store: str):
        self._end_point = endpoint
        self._access_key_id = access_key_id
        self._access_key = access_key
        self._project = project
        self._log_store = log_store
        # 构建client
        self.client = LogClient(self._end_point, self._access_key_id, self._access_key)

    @program_exception
    def _send_log(self, level: str, error_msg: str = '', params: str = ''):
        """
        发送一条log给阿里云log服务
        :param level:
        :param error_msg:
        :param params:
        :return:
        """
        # 阿里日志的空间参数,每个 Logstore 根据 Topic 可以划分成一个或多个子空间,当进行查询时,指定 topic 可以限定查询范围,达到更快速度。
        topic = ''
        # 阿里日志的来源参数,空则默认为访问者IP
        source = ''

        log_item_list = []
        env = os.getenv('APP_ENV', 'local').lower()

        content = ', '.join([env, error_msg, params])
        contents = [(str(level), content[:-1])]
        log_item = LogItem()
        log_item.set_time(int(time.time()))
        log_item.set_contents(contents)
        log_item_list.append(log_item)

        req = PutLogsRequest(self._project, self._log_store, topic, source, log_item_list)
        # 阿里不提供正确记录日志的返回状态,不报错则是成功,控制台在s级别能看到日志信息
        self.client.put_logs(req)

    def log_info(self, error_msg: str = '', params: str = ''):
        """
        发送一条info级别的log
        :param error_msg:
        :param params:
        :return:
        """
        self._send_log(LogLevelEnum.INFO.name, error_msg, params)

    def log_warn(self, error_msg: str = '', params: str = ''):
        """
        发送一条warn级别的log
        :param error_msg:
        :param params:
        :return:
        """
        self._send_log(LogLevelEnum.WARN.name, error_msg, params)

    def log_error(self, error_msg: str = '', params: str = ''):
        """
        发送一条error级别的log
        :param error_msg:
        :param params:
        :return:
        """
        self._send_log(LogLevelEnum.ERROR.name, error_msg, params)


if config['ALILOG_ENABLE']:
    alilog = AliLog(
        config['ALILOG_ENDPOINT'],
        config['ALILOG_ACCESS_KEY_ID'],
        config['ALILOG_ACCESS_KEY'],
        config['ALILOG_PROJECT'],
        config['ALILOG_LOG_STORE']
    )
else:
    alilog = PassObject()

Celery

celery 任务为 celery_tasks.py,在package的 init.py 下

from package.celery_tasks import app as celery_app

__all__ = ('celery_app', )

直接可以运行celery 而不用指定位置

celery -A dataset worker -l INFO &
from celery import Celery

redis_config='redis://127.0.0.1:6379/0'
celery_app = Celery(__name__, backend=redis_config, broker=redis_config)


@celery_app.task(ignore_result=True)
#task_ignore_result 设置全局忽略任务结果
def async_add(x, y):
    return x + y

工厂模式下Flask与Celery结合

from xx.core import create_app
from xx.factory import celery

from xx.statistics.data import save_statistics

app = create_app()
# 关键点,往celery推入flask信息,使得celery能使用flask上下文
app.app_context().push()


if __name__ == '__main__':
    app.run()
from flask import Flask
from .celery import celery_app


def create_app():
    app = Flask(__name__)

    celery_app.conf.update({"broker_url": 'redis://127.0.0.1:6379/0',
                            "result_backend": 'redis://127.0.0.1:6379/0', })
    from .api import bp

    app.register_blueprint(bp)
    return app


class CeleryClient(Celery):
    def __init__(self, app=None):
        super().__init__()
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        super().__init__(app.import_name,
                         broker=app.config['CELERY_BROKER'],
                         backend=app.config['CELERY_BACKEND'])
# celery
celery = CeleryClient()

Flask工厂模式集成使用Celery

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值