How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
Django · Python

Fix MiddlewareNotUsed: ImproperlyConfigured: MIDDLEWARE contains 'myapp.middleware.RequestLogMiddleware' which does not define a __call__ method. in Django

This error means your custom middleware class is missing the required __call__ method or was written for the old-style MIDDLEWARE_CLASSES format. Fix it by ensuring your middleware accepts get_response in __init__ and implements __call__ to process the request, following Django's new-style middleware pattern.

Reading the Stack Trace

Traceback (most recent call last): File "/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) File "/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 57, in load_middleware mw_instance = middleware(handler) File "/venv/lib/python3.11/site-packages/django/utils/deprecation.py", line 116, in __init__ raise ImproperlyConfigured( django.core.exceptions.ImproperlyConfigured: MIDDLEWARE contains 'myapp.middleware.RequestLogMiddleware' which does not define a __call__ method.

Here's what each line means:

Common Causes

1. Old-style middleware class

The middleware was written for the deprecated MIDDLEWARE_CLASSES format using process_request/process_response methods without the new __call__ pattern.

# myapp/middleware.py
class RequestLogMiddleware:
    def process_request(self, request):
        import logging
        logging.info(f'Request: {request.path}')

    def process_response(self, request, response):
        return response

2. Missing __init__ with get_response

The middleware class has a __call__ method but its __init__ does not accept or store the get_response callable.

class RequestLogMiddleware:
    def __init__(self):
        pass  # Missing get_response parameter

    def __call__(self, request):
        return self.get_response(request)  # AttributeError

3. Typo in MIDDLEWARE setting path

The dotted path in MIDDLEWARE points to a module or class that does not exist.

# settings.py
MIDDLEWARE = [
    'myapp.middlware.RequestLogMiddleware',  # Typo: 'middlware'
]

The Fix

Convert the middleware to Django's new-style format: accept get_response in __init__, implement __call__ to handle the request/response cycle. The process_request and process_response hooks are replaced by code before and after calling self.get_response(request).

Before (broken)
# myapp/middleware.py
class RequestLogMiddleware:
    def process_request(self, request):
        import logging
        logging.info(f'Request: {request.path}')

    def process_response(self, request, response):
        return response
After (fixed)
# myapp/middleware.py
import logging

logger = logging.getLogger(__name__)


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

    def __call__(self, request):
        logger.info(f'Request: {request.method} {request.path}')
        response = self.get_response(request)
        logger.info(f'Response: {response.status_code}')
        return response

Testing the Fix

import pytest
from django.test import TestCase, Client, RequestFactory
from myapp.middleware import RequestLogMiddleware


class TestRequestLogMiddleware(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

    def test_middleware_passes_request_through(self):
        def fake_get_response(request):
            from django.http import HttpResponse
            return HttpResponse('OK')

        middleware = RequestLogMiddleware(fake_get_response)
        request = self.factory.get('/test/')
        response = middleware(request)
        assert response.status_code == 200

    def test_middleware_logs_request(self, caplog):
        def fake_get_response(request):
            from django.http import HttpResponse
            return HttpResponse('OK')

        middleware = RequestLogMiddleware(fake_get_response)
        request = self.factory.get('/test/')
        with self.assertLogs('myapp.middleware', level='INFO') as cm:
            middleware(request)
        assert any('/test/' in msg for msg in cm.output)

    def test_middleware_integration(self):
        client = Client()
        response = client.get('/')
        assert response.status_code in (200, 302, 404)

Run your tests:

pytest

Pushing Through CI/CD

git checkout -b fix/middleware-new-style,git add myapp/middleware.py,git commit -m "fix: convert middleware to new-style with __init__ and __call__",git push origin fix/middleware-new-style

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      - run: pip install -r requirements.txt
      - run: python manage.py check
      - run: pytest --tb=short -q

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

pip install bugstack

Step 2: Initialize

import bugstack

bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Run the full test suite locally to confirm the middleware loads correctly.
  2. Open a pull request with the middleware refactor.
  3. Wait for CI checks including Django's system check to pass.
  4. Have a teammate review and approve the PR.
  5. Merge to main and verify the middleware works correctly in staging.

Frequently Asked Questions

BugStack runs Django's system checks, runs your test suite, and validates that the middleware correctly passes requests through the chain before marking it safe to deploy.

BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.

Yes, if your middleware extends django.utils.deprecation.MiddlewareMixin. This compatibility layer wraps old-style methods in the new __call__ pattern, but writing new-style middleware directly is preferred.

Yes, critically. Django processes MIDDLEWARE top-to-bottom for requests and bottom-to-top for responses. Security middleware should come first, and your custom middleware should be placed after Django's built-in middleware.