Use of Middlewares with Class Based Views

Hi,

I have been looking for the answer since 3 days, so if anyone can make things clearer for me, that would be great (I’m new to Django).

So, i created a CBV with a get method that i supposed to render a template and display a message.
class ShowEntityView(TemplateView):
def get(self, request):
pass

This CBV is accessible through a URL that contains 3 arguments (PID, CHECK, HASH).
path('entities/<entity_pid>/<check_id>/<hash_id>/', ShowEntityView.as_view())

The PID is a unique identifier of a database entity.

As i want to verify the existence of the entity before returning a response, i thought of creating a middleware and calling it using a decorator.
@method_decorator([VerifyExistenceAndSetEntity], name='dispatch')

And the middleware looks something like :

class VerifyExistenceAndSetEntity:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return HttpResponseNotFound("reached")
        # response = self.get_response(request)
        # return response

I was expecting to have a 404 page with the message “reached”, but no, i’m having an error :
__call__() got an unexpected keyword argument 'entity_pid'

I tried different approches like using the “MiddlewareMixin” class, but nothing works !

Please tell me what i am doing wrong !

Thank you.

Hi,

I think you are on the wrong track here. You could just use the ORM to ask if the entity exists in your database:
get_object_or_404(Entity,pk=entity_pid)
I guess this is what your Middleware ist trying to do too, you can read about it here: https://docs.djangoproject.com/en/3.0/topics/http/shortcuts/#get-object-or-404

Thank’s for your answer.

You actually showed me how i would verify that my entity exist !

I could easily use “get_object_or_404” within my views, but i wanted to delegate this verification logic to a middleware.

So the problem is that the execution of my middleware raises a “TypeError” exception.

Environment:

Request Method: GET
Request URL: http://localhost:8000/p/entities/75YzAvrTTZiZzxR5SjI0L7WPLUe97QSuR8goSywRuyDwe/F5FD4DS8F51DFD1FS5/b59c3d0d28520d6f6fa569881c520c27421140491b4dbfde464ee43935cfddf7/

Django Version: 3.0.6
Python Version: 3.6.9
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'polymorphic',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'debug_toolbar',
 'corsheaders',
 'main.base',
 'main.modules.auth',
 'main.modules.entities']
Installed Middleware:
['main.base.middlewares.RemoveSensibleHeaders',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'debug_toolbar.middleware.DebugToolbarMiddleware']

Traceback (most recent call last):
  File "/home/yoo/development/myproject/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/yoo/development/myproject/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/yoo/development/myproject/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/yoo/development/myproject/env/lib/python3.6/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/yoo/development/myproject/env/lib/python3.6/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)

Exception Type: TypeError at /p/entities/75YzAvrTTZiZzxR5SjI0L7WPLUe97QSuR8goSywRuyDwe/F5FD4DS8F51DFD1FS5/b59c3d0d28520d6f6fa569881c520c27421140491b4dbfde464ee43935cfddf7/
Exception Value: __call__() got an unexpected keyword argument 'entity_pid'

This really isn’t a function for your middleware. I’m guessing you’re going to have many pages on your site handling many different pages (with possibly) different parameters for each.

The responsibility for handling a specific request belongs in the view. The validation of the data being submitted with that request belongs in the view for that page.

Middleware should be reserved for actions that need to be performed on every (or nearly every) page. It should be able to do its work without knowing anything specific about the view being requested.

Don’t fight the framework. Work with Django, doing things the way that Django wants to do them, not the way you think you might want to do them - you’ll end up a lot happier that way.

1 Like

Ok try it your way:
So __call__ got an unexpected keyword argument handed down. You could try:
__call__(self, request, **kwargs) to catch all keyword arguements that get handed down to the function.

Thank’s for your suggestion.

What if i had a whole section in my web app that involves dozen of class based views that are accessible through URLs that require an app ID argument.

/<app_id>/create
/<app_id>/show
/<app_id>/update
/<app_id>/remove
/<app_id>/comments/create
etc ...

Are you suggesting that i control the existence of <app_id> on every class based view method ?

I thought it would be more interesting to make this control in a middleware and call the middleware before every view by using the middleware decorator. And i didn’t thought i was fighting the framework because the method* decorator exists natively in Django, so why not using it ?

I maybe didn’t show the real content of the middleware, but i assure you that i wouldn’t put in place a middlware just to check the existence of an entity ! :smiley:

I used to do the same thing on Laravel when a group of routes require the same control to allow access to a controller.

So it’s not really a question of software architecture as my middleware will be called before every view that requires an App ID argument. My question is “Why my middleware” (useful or not) raises an exception because of a URL argument ?

Yes, i already tried this, here’s what happened !

I first used “MiddlewareMixin” as a parent class for all my middlewares, worked perfectly on all views that require a POST method and didn’t have any URL arguments !

But then, i had to put in place views that require one or more URL arguments (as shown in my first message), so the problem appeared !

So, i took the content of “MiddlewareMixin” and created my own middleware base class, i added to that class the change that you have suggested.

class BaseMiddleware:
    def __init__(self, get_response=None):
        self.get_response = get_response
        # super().__init__()

    def __call__(self, request, **kwargs):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        response = response or self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response 

The error disapear, but “process_request” and “process_view” are not called, only “process_response” is !

Yes, exactly that.

If you’re using the system-provided generic class based views as your base, the provided get_object method already will raise an Http404 error if the object isn’t found.

1 Like