25👍
Response
is already rendered in the middleware stage so you can’t just change response.data
, you need to rerender it or change rendered content directly.
class RequestLogMiddleWare(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if isinstance(response, Response):
response.data['detail'] = 'I have been edited'
# you need to change private attribute `_is_render`
# to call render second time
response._is_rendered = False
response.render()
return response
The second approach is to change content directly, but in that case builtin REST Framework browser API will not work because template will not render properly.
import json
class RequestLogMiddleWare(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if isinstance(response, Response):
response.data['detail'] = 'I have been edited'
response.content = json.dumps(response.data)
return response
11👍
I don’t suggest editing REST-Framework’s Response with using middleware. I think you should overload REST-Framework’s default JSONRenderer.
Define a custom renderer:
from rest_framework.renderers import JSONRenderer
class CustomJsonRender(JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
if renderer_context:
response = renderer_context['response']
msg = "OK"
code = response.status_code
if isinstance(data, dict):
msg = data.pop('msg', msg)
code = data.pop('code', code)
data = data.pop('data', data)
if code != 200 and data:
msg = data.pop('detail', 'failed')
response.status_code = 200
res = {
'code': code,
'msg': msg,
'data': data,
}
return super().render(res, accepted_media_type, renderer_context)
else:
return super().render(data, accepted_media_type, renderer_context)
Using it in you APiVIew or ViewSet:
class SimpleView(APIView):
renderer_classes = (CustomJsonRender,)
def get(self, request):
# do something
return Response({"id":"xx"})
class SimpleViewSet(ModelViewSet):
renderer_classes = (CustomJsonRender,)
queryset = SomeModel.objects.all()
serializer_class = SomeSerializer
You can edit it golbal in settings as well:
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": ("compent.renders.CustomJsonRender",)
}
Then your response has been changed.
- Django or Ruby on Rails
- Limit a single record in model for django app?
- How to implement sorting in Django Admin for calculated model properties without writing the logic twice?
- Run django application without django.contrib.admin
- How to test Django custom model fields?
6👍
I have a feeling that I found cleaner solution. Here’s how I rewrote the code:
class RequestLogMiddleWare(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_template_response(self, request, response):
if hasattr(response, 'data'):
response.data['detail'] = 'bla-bla-bla'
return response
- Dynamically filter ListView CBV in Django 1.7
- Do RESTful service parameters have to be discoverable?
- Filter on django-import-export
- Django: Custom User Model fields not appearing in Django admin
0👍
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
res = str(response.content)
res += "<h2>add some html!</h2>"
response.content = bytes(res, encoding="UTF8")
return response
response.content type is bytes, so you need first convert it to str,
add something to it, convert it to bytes
and finally assign it to the response.content.