2017-01-27 4 views
2

У меня есть иерархия проверок разрешений и просмотров, которые должны быть защищены ими. Например, рассмотрите эти две проверки.Составляйте декораторы, применяемые к классам.

def has_profile(user): 
    return user.profile is not None 

def is_a_sorcerer(user): 
    return user.profile.category == 'sorcerer' 

Обратите внимание, что это имеет смысл только для вызова is_a_sorcerer если пользователь имеет профиль.

Некоторые виды должны быть доступны для всех, у кого есть профиль, некоторые виды ограничены только магами.

class ProfileView(View): 
    @method_decorator(user_passes_test(has_profile)) 
    def dispatch(self, *args, **kwargs): 
     return super().dispatch(*args, **kwargs) 

class SorcererView(ProfileView): 
    @method_decorator(user_passes_test(is_a_sorcerer)) 
    def dispatch(self, *args, **kwargs): 
     return super().dispatch(*args, **kwargs) 

Однако, обратите внимание, что из-за наследства, is_a_sorcerer будет называться перед темhas_profile и без ошибок.

можно проверить has_profile в is_a_sorcerer:

def is_a_sorcerer(user): 
    return has_profile(user) and user.profile.category == 'sorcerer' 

В то время как это фиксирует ошибку, это приводит к проверке has_profile дважды, и более чем в два уровня проверки накладных накапливает быстро.

Как создать эти декораторы без дублирующего кода? Я все равно хотел бы, чтобы проверки разрешений выполнялись как функции, чтобы их можно было применять к представлениям, основанным на функциях.

+1

В чем проблема с этим наследованием? Если у пользователя нет профиля, он не может быть колдуном, не так ли? В реализации 'is_a_sorcerer' вы должны проверить, есть ли' has_profile'. – afilardo

+0

Использование [разрешений] (https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#custom-permissions) не является вариантом? – afilardo

+0

Добавление декоратора 'permission_required' к представлениям не будет отличаться от' user_passes_test', и все равно придется проверять разрешения дважды. – Koterpillar

ответ

1

На практике я просто проверил бы has_profile в методе is_a_sorcerer. Накладные расходы на проверку has_profile дважды будут незначительными.

def is_a_sorcerer(user): 
    return has_profile(user) and user.profile.category == 'sorcerer' 

Невозможно запустить проверку дважды, но я не думаю, что вы сможете использовать декораторы. Вместо этого вы можете изменить ProfileView так, чтобы он проверял список тестов. Затем переопределите список тестов в подклассе.

class ProfileView(View): 
    user_tests = [has_profile] 

    def dispatch(self, *args, **kwargs): 
     for test_func in self.user_tests: 
      if not test_func(self.request.user): 
       return redirect('login') 
     return super().dispatch(*args, **kwargs) 


class SorcererView(ProfileView): 
    user_tests = ProfileView.user_tests + [is_a_sorcerer] 

Обратите внимание, что это менее условный и, возможно, более хрупкий/подверженный ошибкам. Было бы легко забыть включить ProfileView.user_tests при переопределении user_tests в подклассе.

+0

На практике декораторы выполняют вызовы базы данных, которые не обязательно кэшируются. Подход 'user_tests' - это то, о чем я тоже думал. Жаль, что это не дефолт. – Koterpillar

Смежные вопросы