Django中六个常用的自定义装饰器

本文介绍了Django中几种实用的装饰器,包括@login_required、@group_required、@superuser_only等,展示了如何利用这些装饰器简化权限控制逻辑,提高代码复用性。

装饰器作用

decorator是当今最流行的设计模式之一,很多使用它的人并不知道它是一种设计模式。这种模式有什么特别之处? 有兴趣可以看看Python Wiki上例子,使用它可以很方便地修改对象行为,通过使用类似例中的接口将修改动作封装在装饰对象中。

decorator 可以动态地修改函数、方法或类的功能,而无需创建子类或修改类的源代码。正因为如此,装饰器可以让代码将变得更干净更可读更可维护(这很重要!),并且减少了许多冗余但又不得不写的代码,使我们可以使用单个方法向多个类添加功能。

对于装饰器的重用性和易用性,Django里面的@login_required就是一个很好的例子。使用它只用一句代码就可以检查用户是否通过身份验证,并将未登录用户重定向到登录url。

该装饰器的使用方法如下:

from django.contrib.auth.decorators import login_required

@login_required(login_url='/accounts/login/')
def my_view(request):
    ...

每次用户试图访问 my_view 时,都会进入 login_required 中的代码。

Django装饰器

下面介绍一些个人认为比较有用的,或者是之前使用过的具有积极效果的装饰器。事先声明,如要实现同样的业务场景,并不是只有本文中的方法。Django可以实现各种各样的装饰器,这完全根据您的需要进行定制。

Group Required

有时需要保护一些视图,只允许某些用户组访问。这时就可以使用下面的装饰器来检查用户是否属于该用户组。

from django.contrib.auth.decorators import user_passes_test


def group_required(*group_names):
   """Requires user membership in at least one of the groups passed in."""

   def in_groups(u):
       if u.is_authenticated():
           if bool(u.groups.filter(name__in=group_names)) | u.is_superuser:
               return True
       return False
   return user_passes_test(in_groups)


# The way to use this decorator is:
@group_required('admins', 'seller')
def my_view(request, pk):
    ...

有关此装饰器更多的介绍,可以参考这里

Anonymous required

这个装饰器是参考Django自带的 login_required 装饰器,但是功能是相反的情况,即用户必须是未登录的,否则用户将被重定向到 settings.py 中定义的地址。当我们想要已登录的用户不允许进入某些视图(比如登录)时,非常有用。

def anonymous_required(function=None, redirect_url=None):

   if not redirect_url:
       redirect_url = settings.LOGIN_REDIRECT_URL

   actual_decorator = user_passes_test(
       lambda u: u.is_anonymous(),
       login_url=redirect_url
   )

   if function:
       return actual_decorator(function)
   return actual_decorator


# The way to use this decorator is:
@anonymous_required
def my_view(request, pk):
    ...

有关此装饰器更多的介绍,可以参考这里

Superuser required

这个装饰器和上面的 group_required 类似, 但是它只允许超级用户才能访问视图。

from django.core.exceptions import PermissionDenied


def superuser_only(function):
    """Limit view to superusers only."""

    def _inner(request, *args, **kwargs):
        if not request.user.is_superuser:
            raise PermissionDenied
        return function(request, *args, **kwargs)

    return _inner


# The way to use this decorator is:
@superuser_only
def my_view(request):
    ...

有关此装饰器更多的介绍,可以参考这里

Ajax required

这个装饰器用于检查请求是否是AJAX请求,在使用jQuery等Javascript框架时,这是一个非常有用的装饰器,也是一种保护应用程序的好方法。

from django.http import HttpResponseBadRequest


def ajax_required(f):
    """
    AJAX request required decorator
    use it in your views:
 
    @ajax_required
    def my_view(request):
        ....
 
    """

    def wrap(request, *args, **kwargs):
        if not request.is_ajax():
            return HttpResponseBadRequest()
        return f(request, *args, **kwargs)

    wrap.__doc__ = f.__doc__
    wrap.__name__ = f.__name__
    return wrap


# The way to use this decorator is:
@ajax_required
def my_view(request):
    ...

有关此装饰器更多的介绍,可以参考这里

Time it

如果您需要改进某个视图的响应时间,或者只想知道运行需要多长时间,那么这个装饰器非常有用。

def timeit(method):

   def timed(*args, **kw):
       ts = time.time()
       result = method(*args, **kw)
       te = time.time()
       print('%r (%r, %r) %2.2f sec' % (method.__name__, args, kw, te - ts))
       return result

   return timed


# The way to use this decorator is:
@timeit
def my_view(request):
    ...

有关此装饰器更多的介绍,可以参考这里

自定义功能

下面这个装饰器只是一个示例,测试你能够轻松地检查某些权限或某些判断条件,并100%自己定制。
想象你有一个博客、购物论坛,如果用户需要有很多积分才能发表评论,这是一个避免垃圾信息的好方法。下面创建一个装饰器来检查用户是否已登录并拥有超过10个积分,这样才可以发表评论,否则将抛出一个Forbidden。

from django.http import HttpResponseForbidden


logger = logging.getLogger(__name__)


def user_can_write_a_review(func):
   """View decorator that checks a user is allowed to write a review, in negative case the decorator return Forbidden"""

   @functools.wraps(func)
   def wrapper(request, *args, **kwargs):
       if request.user.is_authenticated() and request.user.points < 10:
           logger.warning('The {} user has tried to write a review, but does not have enough points to do so'.format( request.user.pk))
           return HttpResponseForbidden()

       return func(request, *args, **kwargs)

   return wrapper

转载于:https://www.cnblogs.com/jhao/p/9258892.html

<think> 我们被要求介绍Python自定义装饰器的相关知识。根据引用内容,装饰器是一种在不修改原始函数代码的情况下,动态地扩展函数或类功能的技术。它们通过接受一个函数作为参数并返回一个新的函数来实现。装饰器可以用于日志记录、性能测试、事务处理、权限校验等场景。 引用中提到了几种装饰器的类型: 1. 简单的函数装饰器(无参装饰器) 2. 带参数的装饰器(需要两层嵌套) 3. 类装饰器(通过实现`__call__`方法) 4. 类作为装饰器带参数(需要实现`__init__`和`__call__`,且参数不同) 同时,引用还提到了一些内置装饰器如`@staticmethod`、`@classmethod`、`@property`等,但我们的重点是自定义装饰器。 下面我们将按照以下结构进行介绍: 1. 装饰器基础:无参函数装饰器 2. 带参数的装饰器 3. 类装饰器 4. 装饰器的实际应用场景 5. 装饰器的注意事项 ### 1. 装饰器基础:无参函数装饰器 装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。在装饰器内部,我们可以定义一个新的函数(通常称为包装函数),在这个新函数中调用原始函数,并在调用前后添加额外的功能。 例如,一个记录函数执行时间的装饰器: ```python import time def timer_decorator(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) # 执行原函数 end_time = time.time() print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.6f}秒") return result return wrapper @timer_decorator def my_function(n): s = 0 for i in range(n): s += i return s # 调用被装饰的函数 my_function(1000000) ``` 输出示例: ``` 函数 my_function 执行耗时: 0.078125秒 ``` ### 2. 带参数的装饰器 有时我们需要通过参数来配置装饰器的行为,这时就需要创建一个带参数的装饰器。实际上,带参数的装饰器需要编写一个返回装饰器函数的高阶函数。 例如,一个可以重复执行函数的装饰器,重复次数由参数指定: ```python def repeat(times): # 这是装饰器生成器,参数times指定重复次数 def decorator(func): # 这是实际的装饰器 def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result # 注意:这里只返回最后一次调用的结果 return wrapper return decorator @repeat(times=3) def greet(name): print(f"Hello, {name}!") greet("Alice") ``` 输出: ``` Hello, Alice! Hello, Alice! Hello, Alice! ``` ### 3. 类装饰器装饰器是利用类的`__call__`方法来实现的。当将一个类作为装饰器时,类的实例会变得可调用,从而可以像函数一样工作。 例如,一个记录函数调用次数的类装饰器(引用[3]中的例子): ```python class CountCalls: def __init__(self, func): self.func = func self.calls = 0 def __call__(self, *args, **kwargs): self.calls += 1 print(f"Call {self.calls} of {self.func.__name__}") return self.func(*args, **kwargs) @CountCalls def say_hello(): print("Hello!") say_hello() say_hello() ``` 输出: ``` Call 1 of say_hello Hello! Call 2 of say_hello Hello! ``` ### 4. 实际应用场景 - **日志记录**:记录函数的调用参数、返回值等。 - **性能监控**:如上面的计时装饰器。 - **权限校验**:在Web框架中,检查用户是否登录或具有权限。 - **缓存**:存储函数的结果,避免重复计算(类似于`functools.lru_cache`)。 - **事务处理**:确保数据库操作的原子性(例如,失败时回滚)。 - **错误重试**:当函数执行失败时,自动重试指定的次数。 ### 5. 注意事项 - **保留原函数的元信息**:使用装饰器后,原函数的名称、文档字符串等元信息会被包装函数覆盖。可以使用`functools.wraps`装饰器来保留这些信息。 ```python from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): # ... 额外操作 return func(*args, **kwargs) return wrapper ``` - **装饰器的顺序**:多个装饰器堆叠时,靠近函数定义的装饰器先执行(从下往上)。例如: ```python @decorator1 @decorator2 def my_func(): pass ``` 等价于:`my_func = decorator1(decorator2(my_func))`,所以先执行`decorator2`,再执行`decorator1`。 - **装饰器对函数签名的影响**:如果装饰器没有正确设计,可能会改变函数的签名(参数信息),使用`functools.wraps`可以避免此问题。 ### 总结 自定义装饰器是Python中一种强大的工具,它可以提高代码的复用性,将横切关注点(如日志、性能监测)与业务逻辑分离。掌握装饰器的编写和使用,能够使代码更加简洁、优雅。 §§相关问题§§ 1. 如何保留被装饰函数的元信息(如函数名、文档字符串)? 2. 多个装饰器作用于同一个函数时的执行顺序是怎样的? 3. 装饰器能否装饰类?如何实现? 4. 如何编写一个装饰器用于缓存函数结果? </think> ### Python自定义装饰器详解 装饰器是Python的核心特性之一,它允许在不修改原始函数代码的情况下扩展功能。下面从基础到高级全面讲解自定义装饰器的原理和应用。 #### 一、装饰器本质 装饰器本质上是**接收函数作为参数的高阶函数**,其核心结构如下: ```python def decorator(func): # 接收被装饰函数 def wrapper(*args, **kwargs): # 包裹原函数 # 前置增强功能 result = func(*args, **kwargs) # 执行原函数 # 后置增强功能 return result return wrapper # 返回新函数 ``` #### 二、基础装饰器示例 实现函数调用计数器(引用[3]案例扩展): ```python class CallCounter: def __init__(self, func): self.func = func self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 print(f"第{self.count}次调用 {self.func.__name__}") return self.func(*args, **kwargs) @CallCounter def process_data(data): print(f"处理数据: {data}") process_data("A") # 输出: 第1次调用 process_data → 处理数据: A process_data("B") # 输出: 第2次调用 process_data → 处理数据: B print(process_data.count) # 输出: 2 ``` #### 三、带参数装饰器(双层嵌套) 实现带阈值的执行时间监控: ```python import time def timeit(threshold=0.5): # 外层接收参数 def decorator(func): # 中层接收函数 def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) elapsed = time.time() - start if elapsed > threshold: print(f"⚠️ {func.__name__} 执行超时: {elapsed:.3f}s") else: print(f"✅ {func.__name__} 正常执行: {elapsed:.3f}s") return result return wrapper return decorator @timeit(threshold=0.1) # 设置阈值 def complex_calculation(n): return sum(i*i for i in range(n)) complex_calculation(10**6) ``` 输出示例: ``` ⚠️ complex_calculation 执行超时: 0.347s ``` #### 四、高级应用场景 1. **权限验证装饰器** ```python def requires_login(func): def wrapper(user, *args, **kwargs): if not user.is_authenticated: print("❌ 请先登录") return None return func(user, *args, **kwargs) return wrapper @requires_login def access_secret_data(user): print(f"用户 {user.name} 访问机密数据") ``` 2. **数据库事务装饰器** ```python def transactional(func): def wrapper(*args, **kwargs): print("🚀 开启事务") try: result = func(*args, **kwargs) print("✅ 提交事务") return result except Exception as e: print(f"⛔ 回滚事务: {e}") raise return wrapper ``` 3. **参数验证装饰器** ```python def validate_types(*types): def decorator(func): def wrapper(*args, **kwargs): for i, (arg, t) in enumerate(zip(args, types)): if not isinstance(arg, t): raise TypeError(f"参数 {i} 应为 {t} 类型") return func(*args, **kwargs) return wrapper return decorator @validate_types(int, str) def process(id, name): print(f"处理 {id}: {name}") ``` #### 五、最佳实践与注意事项 1. **保留元信息**:使用`functools.wraps`保持原函数属性 ```python from functools import wraps def decorator(func): @wraps(func) # ← 关键修复 def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper ``` 2. **调试技巧**:通过`__name__`和`__doc__`检查装饰效果 ```python print(process_data.__name__) # 未用wraps时显示"wrapper" ``` 3. **类装饰器优势**(引用[3][4]): - 可维护状态(如计数器) - 更清晰的分层结构 - 支持继承和组合 #### 六、实际应用价值 1. **AOP编程**:实现横切关注点分离[^1] 2. **DRY原则**:消除重复代码[^2] 3. **代码可读性**:通过`@`语法明确功能增强[^5] 4. **框架集成**:Flask路由`@app.route`、Django权限控制等 > "装饰器在提高代码可读性、可维护性和扩展性方面展现出强大能力"[^1],是现代Python开发的必备技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值