python使用装饰器@函数式化django开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python使用装饰器@函数式化django开发相关的知识,希望对你有一定的参考价值。

django是一个python web开发的框架。作为一个框架MVC的架构已经实现起来了。但是编码的时候你经常要进行进一步的抽象。

AOP是一种称为面向切面的开发思想,意思是将部分功能代码在运行时动态的加载到指定位置。最常见的应用是Spring中的依赖注入@Autowired。

而装饰器也可以被看成是一种AOP的实现,但是又有些许的不同,让我们来体会一下。

 

在我们的实例中我们将django中的views.py(其实是controller层)拆出了implement.py(实现)和decorator.py(装饰器)

1、先看我们的views.py。

这里我们只给出一个接口做为演示。

# -*- coding:utf-8 -*-
import json
import logging
import implement
import decorator
from django.views.decorators.csrf import csrf_exempt

logger = logging.getLogger(__name__)

@csrf_exempt
@decorator.impl_wrapper_check_time
@decorator.process_time
@decorator.cache
def alarm_month_daytotal(request):
    if request.method == GET:
        time_str = request.GET.get(time)
        return implement.alarm_month_daytotal(time_str)

alarm_total接口在views.py中的函数alarm_total中并没有返回JSONResponse对象,而是直接返回了调用implement.alarm_total()的结果,看样子是implement.alarm_total()返回了JSONResponse

,我们看看是不是这样。

2、implement实现层

# -*- coding:utf-8 -*-
import json
import logging
import calendar

from rediss import RedisCache
from config import redis_config, alarm_status_mapping, alarm_type_mapping, alarm_level_mapping, combine_module, alarm_module_mapping,alarm_stage_mapping,     alarm_type_config_mapping, month_total_closed_mapping, month_total_tosolve_mapping
from datetime import datetime, timedelta
from dao import Dao as dash_dao
from exceptions import DataEmptyException
import time

logger = logging.getLogger(__name__)

# 按月获取分天报警情况
def alarm_month_daytotal(time_str):
    time_object = datetime.strptime(time_str, %Y-%m)
    month, length = calendar.monthrange(time_object.year, time_object.month)
    ret_list = [0 for i in range(length)]
    items = dash_dao.get_alarms_by_month(time_str)
    if not items:
        raise DataEmptyException([table] %s % alarm_list_table)
    for item in items:
        if not item.alarm_time:
            continue
        ret_list[at_day(item.alarm_time) - 1] += 1
    r = RedisCache(redis_config)
    key_list = r.keys("dmonitor:issue:%s*" % time_str)
    if not key_list:
        return ret_list
    for key in key_list:
        content = r.get(key)
        time_object = datetime.strptime(key.split(:)[2], %Y-%m-%d)
        ret_list[time_object.day - 1] = {
            y: ret_list[time_object.day - 1],
            name: content,
            marker: {
                symbol: url(/data_monitor/static/images/sun.png)
            }
        }
    return ret_list

并没有啊,implement.alarm_total()只返回了一个list对象。这是为什么呢?

原因就在那几个装饰器的封装上。

3、装饰器decorator

impl_wrapper_check_time(func):执行装饰的装饰的func方法,并且对返回进行JSONResponse封装

process_time(func):统计执行时间并打印日志

cache(func):对接口的请求加入缓存

执行顺序,装饰器装饰的顺序,从下往上我们例子里是cache->process_time->impl_wrapper_check_time那么:

1、先执行impl_wrapper_check_time的开始部分

2、然后是process_time时间的start_time记录

3、cache缓存的准备

4、被装饰的函数func

5、cache缓存的返回

6、process_time的end_time记录,并打印时间日志

7、impl_wrapper_check_time返回JSONResponse

异常也是按照这个顺序一级一级的向上抛出。

import logging
import time
import json
import traceback
from rediss import RedisCache
from config import redis_config, cache_timeout, cache_switch
from exceptions import IllegalParamException
from django.http import JsonResponse
from django.http import HttpResponse

logger = logging.getLogger(__name__)

redis = RedisCache(redis_config)def impl_wrapper_check_time(func):
    def wrapper(*args, **kwargs):
        try:
            if args[0].method == GET:
                if not args[0].GET.get(time):
                    raise IllegalParamException(time)
            data = func(*args, **kwargs)
            return JsonResponse({errno: 0, msg: success, data: data})
        except Exception, ex:
            logger.error(traceback.format_exc())
            return JsonResponse({errno: -1, msg: str(ex)})

    return wrapper


def process_time(func):
    def wrapper(*args, **kwargs):
        path = args[0].get_full_path()
        start_time = time.time()
        data = func(*args, **kwargs)
        end_time = time.time()
        logger.info(path: %s, process_time: %s ms % (path, str((end_time - start_time) * 1000)))
        return data
    return wrapper


def cache(func):
    def wrapper(*args, **kwargs):
        if not cache_switch:
            data = func(*args, **kwargs)
            return data
        path = args[0].get_full_path()
        dashboard_cache_key = dashboard:cache:%s % path
        if redis.get(dashboard_cache_key):
            logger.info([Hit Cache] path: %s % path)
            return json.loads(redis.get(dashboard_cache_key))
        data = func(*args, **kwargs)
        redis.set(dashboard_cache_key, json.dumps(data))
        redis.expire(dashboard_cache_key, cache_timeout)
        logger.info([Query] path: %s % path)
        return data
    return wrapper

 


以上是关于python使用装饰器@函数式化django开发的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器迭代器&生成器re正则表达式字符串格式化

19Django开发总结:自带的常用装饰器应用场景及正确使用方法总结

Python 装饰器和类方法和评估——django memoize

装饰器 以及 django 中的应用

在装饰函数完成后,如何让 Python 装饰器运行?

菊花链 Python/Django 自定义装饰器