Как я могу написать свой собственный декоратор в Django? - PullRequest
0 голосов
/ 02 июля 2018

Мой 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 Ответ

0 голосов
/ 02 июля 2018

В декораторе нет ничего волшебного, это функция, которая принимает в качестве входных данных функцию (или класс) для декорирования и вносит в нее некоторые изменения. Если мы посмотрим на декоратор login_required [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 делает не действительно принудительно, что пользователь вошел в систему, хотя во многих случаях это будет так.

EDIT :

Мы можем объединить это с @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
...