Skip to main content
 首页 » 编程设计

python之如何在 Django 中编写自己的装饰器

2025年05月04日32unruledboy

我的models.py文件如下:

from django.contrib.auth.models import User 
 
class Shopkeeper(models.Model): 
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) 
    # ... 
 
 
class Customer(models.Model): 
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) 
# ... 

我有一些 View 只有客户在登录后才能访问,但店主不能。反之亦然。 我该如何为这样的任务编写装饰器?

请您参考如下方法:

装饰器并没有什么神奇之处,它是一个函数,它以要装饰的函数(或类)作为输入,并对其进行一些更改。如果我们看一下 login_required decorator [GitHub] ,我们看到:

def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): 
    """ 
    Decorator for views that checks that the user is logged in, redirecting 
    to the log-in page if necessary. 
    """ 
    actual_decorator = user_passes_test( 
        lambda u: u.is_authenticated, 
        login_url=login_url, 
        redirect_field_name=redirect_field_name 
    ) 
    if function: 
        return actual_decorator(function) 
    return actual_decorator 

因此,我们实际上可以简单地制作一个 user_passes_test 装饰器的特例:

from django.contrib.auth.decorators import user_passes_test 
 
def shopkeeper_required(function=None): 
    def is_shopkeeper(u): 
        return Shopkeeper.objects.filter(user=u).exists() 
    actual_decorator = user_passes_test(is_shopkeeper) 
    if function: 
        return actual_decorator(function) 
    else: 
        return actual_decorator 
 
def customer_required(function=None): 
    def is_customer(u): 
        return Customer.objects.filter(user=u).exists() 
    actual_decorator = user_passes_test(is_customer) 
    if function: 
        return actual_decorator(function) 
    else: 
        return actual_decorator

例如,您可以将其实现为:

@login_required 
@shopkeeper_required 
def some_shopkeeper_view(request): 
    # ... 
    pass 
 
@login_required 
@customer_required 
def some_customer_view(request): 
    # ... 
    pass 

请注意,此 @shopkeeper_required 确实不强制用户登录,尽管在许多情况下都是如此。

编辑:

我们可以将它与 @login_required 合并(通过添加一个参数作为开关来打开或关闭此行为,默认情况下打开),例如:

from django.contrib.auth.decorators import user_passes_test 
 
def shopkeeper_required(function=None<b>, login_required=True</b>, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): 
    def is_shopkeeper(u): 
        <b>if login_required and not u.is_authenticated: 
            return False</b> 
        return Shopkeeper.objects.filter(user=u).exists() 
    actual_decorator = user_passes_test( 
        is_shopkeeper, 
        login_url=login_url, 
        redirect_field_name=redirect_field_name 
    ) 
    if function: 
        return actual_decorator(function) 
    else: 
        return actual_decorator 
 
def customer_required(function=None<b>, login_required=True</b>, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): 
    def is_customer(u): 
        <b>if login_required and not u.is_authenticated: 
            return False</b> 
        return Customer.objects.filter(user=u).exists() 
    actual_decorator = user_passes_test( 
        is_customer, 
        login_url=login_url, 
        redirect_field_name=redirect_field_name 
    ) 
    if function: 
        return actual_decorator(function) 
    else: 
        return actual_decorator