python使用裝飾器@函數式化django開發

django是一個python web開發的框架。做爲一個框架MVC的架構已經實現起來了。可是編碼的時候你常常要進行進一步的抽象。python

AOP是一種稱爲面向切面的開發思想,意思是將部分功能代碼在運行時動態的加載到指定位置。最多見的應用是Spring中的依賴注入@Autowired。web

而裝飾器也能夠被當作是一種AOP的實現,可是又有些許的不一樣,讓咱們來體會一下。redis

 

在咱們的實例中咱們將django中的views.py(實際上是controller層)拆出了implement.py(實現)和decorator.py(裝飾器)django

一、先看咱們的views.py。json

這裏咱們只給出一個接口作爲演示。緩存

# -*- 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架構

,咱們看看是否是這樣。app

二、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對象。這是爲何呢?函數

緣由就在那幾個裝飾器的封裝上。

三、裝飾器decorator

impl_wrapper_check_time(func):執行裝飾的裝飾的func方法,而且對返回進行JSONResponse封裝

process_time(func):統計執行時間並打印日誌

cache(func):對接口的請求加入緩存

執行順序,裝飾器裝飾的順序,從下往上咱們例子裏是cache->process_time->impl_wrapper_check_time那麼:

一、先執行impl_wrapper_check_time的開始部分

二、而後是process_time時間的start_time記錄

三、cache緩存的準備

四、被裝飾的函數func

五、cache緩存的返回

六、process_time的end_time記錄,並打印時間日誌

七、impl_wrapper_check_time返回JSONResponse

執行順序說明:

一、異常也是按照這個順序一級一級的向上拋出。

二、最終由impl_wrapper_check_time處理異常,返回errno:0或者-1。

三、先執行完緩存的返回,再執行時間的統計,這樣能夠明顯觀察到緩存對處理時間性能上的提高。

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
相關文章
相關標籤/搜索