Restricting Views via LoginRequiredMixin and LoginRequiredMiddleware.

Hello there! I’d like to ask a few questions regarding restricting views.

Currently, most of my views look a bit like this:

class ExampleView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
    fields = ["field_a", "field_b", "field_c"]
    model = ExampleModel
    permission_required = ["examples.add_examplemodel"]
    success_url = reverse_lazy("examples:list")
    template_name = "examples/create.html"

I’m checking not only if a user is authenticated, but also if that user has permission to access a given resource.

Out of all my views, I think only 2 or 3 are “public views” (AnonymousUser can access them).

With that said, I’ve been using LoginRequiredMixin and PermissionRequiredMixin because Django provides them out of the box and the documentation is pretty good (Using the Django authentication system | Django documentation | Django). Although I’ve never seen them being used that way (class SomeView(LoginRequiredMixin, PermissionRequiredMixin, ...):) in the docs or by 3rd party libs, I assume there’s nothing wrong with this approach. Both inherit from AccessMixin, so I guess it’s okay. Please correct me if I’m wrong.

I’d like to upgrade that set up to something more developer friendly by adding a middleware LoginRequiredMiddleware instead of adding LoginRequiredMixin to most of my views.

Can you guys give me some feedback on my current configuration (see below)?

  • Does it look good? See any issues?
  • Is there a better way to do this?
  • LoginRequiredMixin vs. LoginRequiredMiddleware … any reason to prefer one over the other?
  • Should I be using if public_view in path: instead of if path.startswith(public_view): ?
  • Does the middleware order look good?
  • Do you know of any reputable libraries that implement something similar?
# settings.py

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "myapp.middleware.LoginRequiredMiddleware",   # <<<==========
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

PUBLIC_VIEWS = [
    "app1:login",
    "app1:signup",
    "app2:landing",
]
# middleware.py

class LoginRequiredMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.public_views = [reverse(view) for view in settings.PUBLIC_VIEWS]

    def __call__(self, request):
        path = request.get_full_path()
        is_public_view = False
        for public_view in self.public_views:
            if path.startswith(public_view):
                is_public_view = True
                break
        if not is_public_view:
            if not request.user.is_authenticated:
                return redirect_to_login(path)
            if path.startswith(reverse("admin")) and not request.user.is_staff:
                raise PermissionDenied("Some message")
        return self.get_response(request)

Any feedback is appreciated. Thank you!

You don’t need both LoginRequired and PermissionsRequired. Unless you’ve done something really unusual, AnonymousUser is never going to be granted permissions so LoginRequired is redundant.

I see your point. So, if I need to restrict a view called AnotherExampleView in a way that any authenticated user can access it, I can do something like this:

class AnotherExampleView(PermissionRequiredMixin, TemplateView):
    # ...
    permission_required = []

Right? I mean, AnotherExampleView only requires the user to be authenticated, but it does not require any kind of permission. I can just leave permission_required as an empty list and that’s that.

No, for that you would use LoginRequiredMixin.

It’s that you never need to use both LoginRequiredMixin and PermissionRequiredMixin for the same view.

I see. Thank you so much for the help!