__init__() missing 1 required positional argument: 'get_response' - django

I use Django 1.11 and got this error
class TenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
set_tenant_schema_for_request(request)
response = self.get_response(request)
return response
I tried to fix that:
class TenantMiddleware:
def process_response(self, request, response):
set_tenant_schema_for_request(request)
return response
is this a correct equivalent using "new" middleware style?

Your first example is example of new-style middleware, and second one is example of old-style middlewares. If you still want to use process_response(request, response) in new-style middlewares you should use django.utils.deprecation.MiddlewareMixin class, as a parent class of your middleware class.
from django.utils.deprecation import MiddlewareMixin
# old-style middleware usage in new versions of django
class TenantMiddleware(MiddlewareMixin):
def process_response(self, request, response):
set_tenant_schema_for_request(request)
return response
If you use new-style middlewares,,,
# new-style middleware usage and explanation
class TenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# implement process_request(request) logic here
response = self.get_response(request)
# implement process_response(request, response) logic here
return response # returns HttpResponse or subclasses here.
In new-style middlewares, code before self.get_response(request) is same as process_request(request) and code after self.get_response(request) is same as process_response(request, response).
Hope, it helps you.

Related

How can I handle POST data in django middleware?

I have Django middleware to handle POST request.
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, req):
response = self.get_response(req)
# want to do something with 'r.body',
# but it is not able to be read
return response
As the request body is already read in get_response, I cannot read it again in middleware.
Tried copy.copy(), but no luck since the copied stream references same object of the original one. copy.deepcopy() raises an exception.
How can I handle POST data in middleware?
I want to handle all requests, so implementing the logic in every view is not ideal.
Found solution
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, req):
req.body # just add this line BEFORE get_response
response = self.get_response(req)
return response

Implement Django middleware for response behaviour

I'm a bit confused by the middleware description in the official django docs and can't seem to wrap my head around it.
When implementing class-based middleware it looks something like this (ex. from docs):
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
The middleware gets called for requests as well as for response. This is my first question:
How do I implement different behavior depending on if I'm processing a request or a response?
Also, and this is related, I'm a bit confused because there also seems to be an old deprecated way of writing middleware where it was possible to overwrite the methods process.request() and process_response(). I'm not sure if this way of writing middleware is inadvisable now. Usually, the Django docs are spot on but this part is a little confusing (to me).
Can someone clear this up for me?
Basically the codes before responses = self.get_response(request) should process the request. self.get_response() will get the response from view, then codes after that should process the response. Finally you need to return the response from the function. Here is a visualization:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Codes here will process the request
# -----------------------------------
response = self.get_response(request)
# -----------------------------------
# Codes here will process the response
return response

Why Django doesnt have Error Page Handler for 405 - Method Not Allowed?

When i read Django documentation i found only handlers for:
400,
403,
404,
500 errors.
Why 405 error handler doesn't exists but decorators like require_POST() is in common use?
The point is, what is proper way to create custom error page for Method Not Allowed error ?
I resolve my problem using Django Middleware maybe this will help someone
from django.http import HttpResponseNotAllowed
from django.shortcuts import render
class HttpResponseNotAllowedMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if isinstance(response, HttpResponseNotAllowed):
return render(request, '405.html', status=405)
return response
If you want a different response for each view, you can overwrite your function like:
class someView(View):
....
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("Not available")
If you wanna know what the function does check https://github.com/django/django/blob/master/django/views/generic/base.py#L90

How to add an HTTP header to all Django responses

I'd like to add a few headers to all responses that my Django website returns. Is there a way to do this (besides adding a wrapper to the render function)?
Yes, you should have a look at middlewares.
yourapp/middleware.py
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['X-My-Header'] = "my value"
return response
yourproject/settings.py
MIDDLEWARE = [
...,
'yourapp.middleware.MyMiddleware',
...,
]
When returning JsonResponse.
from django.http import JsonResponse
data = {'key','value'} # some data
response = JsonResponse(data,status=200)
response['Retry-after'] = 345 # seconds
response['custom-header'] = 'some value'
return response

Accessing the current view class instance in Django middleware

Question:
I'm trying to access an attribute of the view instance in the middleware layer.
For example, given a class-based view like this:
# views.py
class MyView(View):
my_attribute = 'something'
I'd love to be able to get a handle on my_attribute in the middleware by doing something like this:
# middleware.py
def process_view(self, request, view_func, view_args, view_kwargs):
my_attribute = request.view.my_attribute
Of course, this does not work because Django doesn't expose the view instance through the request object. Is there a way to get this accomplished?
Thanks!
My first attempt:
I initially figured that the process_view() method might be a good place to do this. Unfortunately, the view_func argument it receives contains a function -- the output of MyView.as_view() -- rather than the view instance itself. From the Django docs:
process_view(self, request, view_func, view_args, view_kwargs)
...view_func is the Python function that Django is about to use. (It’s the actual function
object, not the name of the function as a string.)...
My second attempt:
A handle to the view instance is available in process_template_response() method, but it's pretty awkward, and, in any case, I'd like to be able to work with my_attribute at an earlier point in the middleware stack. But this does work:
def process_template_response(self, request, response):
my_attribute = response.context_data['view'].my_attribute
There is no built-in way to do this, but here is a solution given to me by a kindly user on the django-users mailing list. I'm reposting his suggestion here in case anyone else is trying to do the same thing.
This is useful if:
you want to identify properties of the current view in your middleware and perform processing accordingly, and;
for various reasons you don't want to use mixins or decorators to accomplish similar results.
This inspects the view_func object passed to the process_view() middleware hook and determines and imports the the appropriate view class.
# middleware.py
from myutils import get_class
def process_view(self, request, view_func, view_args, view_kwargs):
view = get_class(view_func.__module__, view_func.__name__)
view.my_attribute
Then your get_class() definition:
# myutils.py
from django.utils import importlib
def get_class(module_name, cls_name):
try:
module = importlib.import_module(module_name)
except ImportError:
raise ImportError('Invalid class path: {}'.format(module_name))
try:
cls = getattr(module, cls_name)
except AttributeError:
raise ImportError('Invalid class name: {}'.format(cls_name))
else:
return cls
Using decorators, there are quite some ways to achieve the desired behavior.
1. If you only want to mark a class for the middleware to do something
from django.utils.decorators import classonlymethod
def special_marker(class_view):
def as_view(cls, **initkwargs):
view = super(cls, cls).as_view(**initkwargs)
view.special_marker = True
return view
return type(class_view.__name__, (class_view,), {
'as_view': classonlymethod(as_view),
})
#special_marker
class MyView(View):
pass
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
special_marker = getattr(view_func, 'special_marker', False)
if special_marker:
# Do something
2. If you want to pass some data to the middleware that you don't need in the view
from django.utils.decorators import classonlymethod
def tell_middleware(**kwargs):
def wrapper(class_view):
def as_view(cls, **initkwargs):
view = super(cls, cls).as_view(**initkwargs)
for k, v in kwargs.items():
setattr(view, k, v)
return view
return type(class_view.__name__, (class_view,), {
'as_view': classonlymethod(as_view),
})
return wrapper
#tell_middleware(my_attribute='something')
class MyView(View):
pass
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
my_attribute = getattr(view_func, 'my_attribute', 'default value')
if my_attribute == 'something':
# Do something
3. If you want to expose some view attributes to the middleware
from django.utils.decorators import classonlymethod
def expose_to_middleware(*args):
def wrapper(class_view):
def as_view(cls, **initkwargs):
view = super(cls, cls).as_view(**initkwargs)
for attr in args:
setattr(view, attr, getattr(class_view, attr)
return view
return type(class_view.__name__, (class_view,), {
'as_view': classonlymethod(as_view),
})
return wrapper
#expose_to_middleware('my_attribute', 'my_other_attribute')
class MyView(View):
my_attribute = 'something'
my_other_attribute = 'else'
unexposed_attribute = 'foobar'
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
my_attribute = getattr(view_func, 'my_attribute', 'default value')
if my_attribute == 'something':
# Do something
Another solution could be to create a new View class:
from django.views.generic.base import View
class AddClassView(View):
#classonlymethod
def as_view(cls, **initkwargs):
view = super(AddClassView, cls).as_view(**initkwargs)
view.cls = cls
return view
And use this in your class based view:
# views.py
class MyView(AddClassView):
my_attribute = 'something'
Then you do the following in the middleware:
# middleware.py
def process_view(self, request, view_func, view_args, view_kwargs):
view_func.cls.my_attribute # 'something'
This method is used in the Django REST Framework(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L94-L104)
If it depends on the view, it should probably be a mixin of that view. If you're doing something like a menu that depends on the active view, I'd do a reverse lookup of the current URL name:
see a previous answer about using URL name lookup of the current URL