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
Here's what each line means:
- File "/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 57, in load_middleware: Django loads all middleware classes at startup and validates they follow the new-style middleware protocol.
- mw_instance = middleware(handler): Django passes the next handler to the middleware constructor, expecting the middleware to store it and call it in __call__.
- raise ImproperlyConfigured(: The middleware class does not implement the required __call__ method, so Django cannot use it in the middleware chain.
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).
# 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
# 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:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- 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:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- 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)
- Run the full test suite locally to confirm the middleware loads correctly.
- Open a pull request with the middleware refactor.
- Wait for CI checks including Django's system check to pass.
- Have a teammate review and approve the PR.
- 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.