【问题标题】:Django Custom Decorator - Attribute ErrorDjango 自定义装饰器 - 属性错误
【发布时间】:2018-11-21 22:54:39
【问题描述】:

编写了我自己的装饰器,它为用户获取可用的报告,并检查视图是否在这些可用的报告中。

这是我的装饰器:

from functools import wraps
from django.http import HttpResponseForbidden


def can_access(a=None):
    def _can_access(view_func):
        def access(request, *args, **kwargs):
            if not request.user.get_reports().filter(codename=a).exists():
                return HttpResponseForbidden()
            return view_func(request, *args, *kwargs)
        return wraps(view_func)(access)
    return _can_access

给我这个错误:

'function'对象没有属性'status_code'

views.py

    import json

from datetime import datetime, timedelta, date
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest

from reporting.enums import ReportFrequency
from base.helpers import get_begin_of_day, get_end_of_day
from billing.helpers import get_fake_usage_by_type, update_license_usage_data
from billing.helpers import get_fake_usage_of_ports, get_fake_usage_by_type, update_license_usage_data, \
    get_usage_by_type, get_fake_minutes_use_by_type
from reporting.enums import ReportFrequency
from .forms import UsageByTypeForm, LAST_MONTH
from reporting.decorators import can_access

@login_required
@can_access
def view_update_license_usage_data(request):
    update_license_usage_data()
    return HttpResponse()


@login_required
@can_access
def billing_home(request):

    return render(
        request,
        'billing_list.html',
        {'section': 'billing_index',}
    )


@login_required
@can_access
def usage_by_type(request):
    user = request.user
    client = user.client

    form = UsageByTypeForm(
        data=request.GET or None,
        user=request.user,
        initial={
            'frequency': ReportFrequency.MONTHLY,
            'begin': (datetime.now()-timedelta(days=365)).date(),
            'end': datetime.now().date()
        }
    )

    return render(
        request,
        'usage_by_type_of_source.html',
        {'form': form}
    )


@login_required
@can_access
def fake_usage_by_type_json(request):
    form = UsageByTypeForm(
        data=request.GET,
        user=request.user,
        initial = {
            'begin': datetime.now()-timedelta(days=365),
            'end': datetime.now()
        }
    )
    form.is_valid()
    data = get_fake_usage_by_type(
        request=request,
        data=form.cleaned_data,
    )
    return JsonResponse(data=data, safe=False)


@login_required
@can_access
def usage_by_type_json(request):
    user = request.user
    form = UsageByTypeForm(
        data=request.GET,
        user=user,
    )
    if not form.is_valid():
        return HttpResponseBadRequest('Bad request')

    begin = get_begin_of_day(form.cleaned_data['begin'])
    end = get_end_of_day(form.cleaned_data['end'])
    clients = form.cleaned_data.get('clients', [])
    mcus = form.cleaned_data.get('mcus', [])
    data = get_usage_by_type(
        user=request.user,
        begin=user.get_utc_time(begin),
        end=user.get_utc_time(end),
        freq=form.cleaned_data['frequency'],
        client_ids=clients,
        mcu_ids=mcus,
    )
    return JsonResponse(data=data, safe=False)


@login_required
@can_access
def usage_of_ports(request):
    user = request.user
    client = user.client

    form = UsageByTypeForm(
        data=request.GET or None,
        user=request.user,
        initial={
            'frequency': ReportFrequency.MONTHLY,
            'begin': (datetime.now()-timedelta(days=365)).date(),
            'end': datetime.now().date()
        }
    )

    return render(
        request,
        'usage_of_ports.html',
        {'form': form}
    )


@login_required
@can_access
def fake_usage_of_ports_json(request):
    form = UsageByTypeForm(
        data=request.GET,
        user=request.user,
        initial = {
            'begin': datetime.now()-timedelta(days=365),
            'end': datetime.now()
        }
    )
    form.is_valid()
    data = get_fake_usage_of_ports(
        request=request,
        data=form.cleaned_data,
    )
    return JsonResponse(data=data, safe=False)


@login_required
@can_access
def usage_of_ports_json(request):
    form = UsageByTypeForm(
        data=request.GET,
        user=request.user,
    )
    if not form.is_valid():
        return HttpResponseBadRequest('Bad request')

    data = get_usage_by_type(
        user=request.user,
        begin=form.cleaned_data['begin'],
        end=form.cleaned_data['end'],
        client_ids=form.cleaned_data['clients'],
        mcu_ids=form.cleaned_data['mcus'],
        types=None,
    )
    return JsonResponse(data=data, safe=False)


@login_required
@can_access
def minutes_use_by_type(request):
    user = request.user
    client = user.client

    form = UsageByTypeForm(
        data=request.GET or None,
        user=request.user,
        initial={
            'frequency': ReportFrequency.MONTHLY,
            'begin': (datetime.now()-timedelta(days=365)).date(),
            'end': datetime.now().date()
        }
    )

    return render(
        request,
        'minute_use_by_type.html',
        {'form': form}
    )


"""@login_required
@can_access
def minutes_use_by_type_json(request):
    form = UsageByTypeForm(
        data=request.GET,
        user=request.user,
    )
    if not form.is_valid():
        return HttpResponseBadRequest('Bad request')

    data = get_minutes_use_by_type(
        user=request.user,
        begin=form.cleaned_data['begin'],
        end=form.cleaned_data['end'],
        client_ids=form.cleaned_data['clients'],
        mcu_ids=form.cleaned_data['mcus'],
        types=None,
    )
    return JsonResponse(data=data, safe=False)"""


@login_required
@can_access
def fake_minutes_use_by_type_json(request):
    form = UsageByTypeForm(
        data=request.GET,
        user=request.user,
        initial = {
            'begin': datetime.now()-timedelta(days=365),
            'end': datetime.now()
        }
    )
    form.is_valid()
    data = get_fake_minutes_use_by_type(
        request=request,
        data=form.cleaned_data,
    )
    return JsonResponse(data=data, safe=False)

装饰器在另一个应用程序中(报告)

发生了什么以及如何解决?

PD。我在同一个应用程序的视图(报告)它工作没有问题。

Traceback:
File "/Users/latin/Documents/booking_center/env/lib/python3.6/site-packages/django/core/handlers/base.py" in get_response
  223.                 response = middleware_method(request, response)
File "/Users/latin/Documents/booking_center/env/lib/python3.6/site-packages/django/middleware/locale.py" in process_response
  39.         if (response.status_code == 404 and not language_from_path

Exception Type: AttributeError at /reports/billing/time_usage_per_licence/
Exception Value: 'function' object has no attribute 'status_code'

【问题讨论】:

  • 请张贴(相关部分)回溯。
  • 你应该用@can_access()装饰它。

标签: python django python-decorators


【解决方案1】:

你用错误的方式装饰它。您应该通过调用can_access(..) 来装饰函数,例如:

@login_required
@can_access()  # with brackets
def view_update_license_usage_data(request):
    # ...
    pass

@login_required
@can_access(a='some_a_value')  # with brackets
def billing_home(request):
    # ...
    pass

(也适用于其他视图功能)。

这应该发生,因为can_access 本身实际上不是一个装饰器函数。 can_access 是一个工厂,它产生一个装饰器函数。确实:

def can_access(a=None):  # function producing the decorator
    def _can_access(view_func):  # the actual decorator
        def access(request, *args, **kwargs):  # the new (decorated) function
            if not request.user.get_reports().filter(codename=a).exists():
                return HttpResponseForbidden()
            return view_func(request, *args, *kwargs)
        return wraps(view_func)(access)  # return (decorated) function
    return _can_access # return *decorator

如果我们在装饰器语句中使用括号,Python 将使用can_access 本身装饰view。因此a=view_update_license_usage_data,“decorated”函数就是_can_access(view_func)

因此,request 对象将通过view_func 参数传递。这个“视图”不会返回HttpResponse,而是一个函数(access(..) 函数),并且这个函数没有status_code,因此 Django 将无法实际产生 HTTP 响应,因为它不知道要做什么填写为状态、消息等。

而且由于我们都喜欢装饰,通常最好用 @wraps 装饰器来装饰被装饰的函数:

def can_access(a=None):  # function producing the decorator
    def _can_access(view_func):  # the actual decorator
        @wraps(view_func)
        def access(request, *args, **kwargs):  # the new (decorated) function
            if not request.user.get_reports().filter(codename=a).exists():
                return HttpResponseForbidden()
            return view_func(request, *args, *kwargs)
        return access  # return (decorated) function
    return _can_access # return *decorator

【讨论】:

  • 最后是@can_access(codename_of_report),但你的评论让我明白了。非常感谢!
  • @GuillermoGonzalez:是的,我们的想法是使用参数a 调用它,或者如果将其留空,则使用None。但我会更新答案以使其更清楚。
猜你喜欢
  • 2018-05-20
  • 2018-07-20
  • 2013-09-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多