如何在 Django 中编写自己的装饰器?
Posted
技术标签:
【中文标题】如何在 Django 中编写自己的装饰器?【英文标题】:How can I write my own decorator in Django? 【发布时间】:2018-12-10 04:09:39 【问题描述】:我的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)
# ...
我有一些视图,只有客户在登录后才能访问,但店主不能。反之亦然。 我怎样才能为这样的任务编写装饰器?
【问题讨论】:
【参考方案1】:装饰器并没有什么神奇之处,它是一个将要装饰的函数(或类)作为输入并对其进行一些更改的函数。如果我们查看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, 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
【讨论】:
如何将@login_required
与此自定义装饰器合并?
actual_decorator = user_passes_test(u.is_authenticated and is_shopkeeper)
会起作用吗?
不!因为那时没有u
,u
存在是因为lambda
表达式,所以应该是user_passes_test(lambda u: u.is_authenticated and is_shopkeeper(u))
。
'User' object has no attribute 'login_required'
当客户已经登录并且通过了对店主可以访问的视图的请求时,您编辑的代码会给出上面给出的错误。
@AbidMehmood:嗨,我打错了。现在已修复:)。以上是关于如何在 Django 中编写自己的装饰器?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 user_passes_test 装饰器可调用函数中传递 Django 请求对象
如何从django中具有@property装饰器的模型类序列化函数
使用 djangorestframework 装饰器时如何调试 django ajax 函数?
Django 的 @login_required 装饰器在人们未注册时将他们重定向到 /accounts/login。如何更改此网址?