如何在 Django 中编写自己的装饰器? [英] How can I write my own decorator in Django?

查看:38
本文介绍了如何在 Django 中编写自己的装饰器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的models.py文件如下:

from django.contrib.auth.models 导入用户类店主(models.Model):用户 = 模型.OneToOneField(用户,on_delete=models.CASCADE,primary_key=True)# ...类客户(模型.模型):用户 = 模型.OneToOneField(用户,on_delete=models.CASCADE,primary_key=True)# ...

而且我有一些视图,只有客户登录后才能访问,而店主则不能.反之亦然.如何为此类任务编写装饰器?

解决方案

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

<块引用>

def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):"""检查用户是否登录的视图装饰器,重定向如有必要,请转到登录页面."""actual_decorator = user_passes_test(lambda u: u.is_authenticated,登录网址=登录网址,redirect_field_name=redirect_field_name)如果功能:返回actual_decorator(函数)返回actual_decorator

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

 from django.contrib.auth.decorators import user_passes_testdef shopkeeper_required(功能=无):def is_shopkeeper(u):返回 Shopkeeper.objects.filter(user=u).exists()actual_decorator = user_passes_test(is_shopkeeper)如果功能:返回actual_decorator(函数)别的:返回actual_decoratordef customer_required(功能=无):def is_customer(u):返回 Customer.objects.filter(user=u).exists()actual_decorator = user_passes_test(is_customer)如果功能:返回actual_decorator(函数)别的:返回actual_decorator

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

@login_required@shopkeeper_requireddef some_shopkeeper_view(请求):# ...经过@需要登录@customer_requireddef some_customer_view(请求):# ...经过

请注意,这个 @shopkeeper_required 并没有真正强制用户登录,尽管在许多情况下会如此.

编辑:

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

 from django.contrib.auth.decorators import user_passes_testdef shopkeeper_required(function=None, login_required=True, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):def is_shopkeeper(u):如果 login_required 而不是 u.is_authenticated:返回错误返回 Shopkeeper.objects.filter(user=u).exists()actual_decorator = user_passes_test(is_shopkeeper,登录网址=登录网址,redirect_field_name=redirect_field_name)如果功能:返回actual_decorator(函数)别的:返回actual_decoratordef customer_required(function=None, login_required=True, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):def is_customer(u):如果 login_required 而不是 u.is_authenticated:返回错误返回 Customer.objects.filter(user=u).exists()actual_decorator = user_passes_test(is_customer,登录网址=登录网址,redirect_field_name=redirect_field_name)如果功能:返回actual_decorator(函数)别的:返回actual_decorator

My models.py file is as follow:

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)
# ...

And I have some views which only Customers can access after login, but Shopkeepers cannot. And vice versa. How can I write decorator for such task?

解决方案

There is nothing magical about a decorator, it is a function that takes as input the function (or class) to decorate, and makes some changes to it. If we look at the login_required decorator [GitHub], we see:

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

We can thus actually simply make a special case of the user_passes_test decorator:

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

You can then for example implement it as:

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

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

Note that this @shopkeeper_required does not really enforces that the user is logged in, although in many cases that will be the case.

EDIT:

We can merge this with a @login_required (by adding a parameter that acts as a switch to turn this behavior on or off, by default on), like:

from django.contrib.auth.decorators import user_passes_test

def shopkeeper_required(function=None, login_required=True, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
    def is_shopkeeper(u):
        if login_required and not u.is_authenticated:
            return False
        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, login_required=True, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
    def is_customer(u):
        if login_required and not u.is_authenticated:
            return False
        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

这篇关于如何在 Django 中编写自己的装饰器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆