Search code examples
pythondjangodecoratormixins

Converting a function decorator to a Class Mixin django


I tried to convert a cutoms decorator from a function to a Class Mixin because I use CVB and I wanted to inherit from this Mixin to check for users status for some pages. I have this decorator which check whether the user is authenticated or not, if the user is authenticated and tries to access a page that has this decorator it will be redirected to the dashboard if is not then il will have the access to acces the page. I wrote this class as a class version of the decorator and if the user is logged in then it works ok but if it it's not and tries to acces that page it give me this error:

ImproperlyConfigured at /auth/login/ UserLoginView is missing the permission_required attribute. Define UserLoginView.permission_required, or override UserLoginView.get_permission_required().

this is the decorator:

def is_authenticated(view_func):
    def wrapper_func(request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('dashboard')
        else:
            return view_func(request, *args, **kwargs)
    return wrapper_func

class version:

class IsAuthenticatedMixin(PermissionRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('dashboard')
        return super().dispatch(request, *args, **kwargs)

the views which inherits this mixin

class IndexFormView(IsAuthenticatedMixin, CreateView):
    permission_required = 'view'
    template_name = 'home/index.html'
    form_class = NewsletterForm

    def post(self, request, *args, **kwargs):
        email = request.POST['email']
        if Newsletter.objects.filter(email=email).exists():
            messages.warning(
                request, 'This email is already subscribed in our system')
        else:
            Newsletter.objects.create(
                email=email)
            messages.success(request,
                             'Your email was subscribed in our system, you\'ll hear from us as soon as possible !')
        return super().post(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['testimonials'] = Testimonial.objects.order_by('-created_date')[:9]
        return context


class AboutTemplateView(IsAuthenticatedMixin, TemplateView):
    template_name = 'home/about.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['banner_page_title'] = 'About Us'
        context['page_location'] = 'home / about'
        return context

class UserResgistrationCreateView(IsAuthenticatedMixin, CreateView):
    template_name = 'home/auth/register.html'
    form_class = UserRegistrationForm
    success_url = reverse_lazy('login')


class UserLoginView(IsAuthenticatedMixin, LoginView):
    template_name = 'home/auth/login.html'


Solution

  • The reason this happen is because you subclass from the PermissionRequiredMixin [Django-doc], this thus means that in all views where you use IsAuthenticatedMixin, you need to provide a permission_required class attribute. The error here is about the UserLoginView:

    class UserLoginView(IsAuthenticatedMixin, LoginView):
        template_name = 'home/auth/login.html'
        permission_required = …

    where you thus need to enter a value for the . But the same problem will occur at other class-based views where you sublcass from IsAuthenticatedMixin, and you do not provide a value for permission_required.

    It is however why you write a custom mixin here. What you are doing is implemented by the LoginRequiredMixin [Django-doc]. The only thing that is different is the redirect url, but you can specify this:

    from django.contrib.auth.mixins import LoginRequiredMixin
    from django.urls import reverse_lazy
    
    class IsAuthenticatedMixin(LoginRequiredMixin):
        login_url = reverse_lazy('dashboard')
        redirect_field_name = None

    In the view where you need to use a permission, you can also subclass the PermissionRequiredMixin:

    from django.contrib.auth.mixins import PermissionRequiredMixin
    
    class IndexFormView(IsAuthenticatedMixin, PermissionRequiredMixin, CreateView):
        permission_required = 'view'
        template_name = 'home/index.html'
        form_class = NewsletterForm