问题
I want to overwrite Response
class of django rest framework so that response back responsive dictionary contain three parameter message
, status
and data
Hello dears all
I try to change Response Class
in DRF
to pass two extra parameter ( message, status ) plus data provide by DRF serializer. message
pass the message like Done
, User Created
or etc and status
pass the message like fail
or success
or etc message and this message useful for reserve special code between frontend and backend.
I want to if don't set this parameter return empty character or null result back to client side
for example in success mode:
{
'data': {
'value_one': 'some data',
'value_two': 'some data',
'value_three': [
'value', 'value', 'value'
],
},
}
'message': 'Done',
'status': 'success',
}
and in failure mode:
{
'data': ['any error message raise by serializer',]
'message': 'Create User Failed',
'status': 'failure',
}
I search about my question and found this solution:
if i inheritance DRF Response Class
in my class and overwrite __init__
method and get message, data and status in this method and call init of parent with own data structure and use this responsive class in my functionality like this implement:
from rest_framework.response import Response
class Response(Response):
def __init__(self, data=None, message=None, data_status=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
data_content = {
'status': data_status,
'data': data,
'message': message,
}
super(Response, self).__init__(
data=data_content,
status=status,
template_name=template_name,
headers=headers,
exception=exception,
content_type=content_type
)
in success mode call:
return Response(data=serializer.data, message='Done', data_status='success', status=200)
in failure mode call:
return Response(data=serializer.errors, message='Create User Failed', data_status='failure', status=400)
and use own Response class in all views class
we had problem
in this solution: if we use GenericViews Class
must be overwrite all http methods we used in view's logic and call own class and this is DRY!!
and other solution i found. in serialized layer, we have abstract method def to_representation(self, instance):
in Serializer
class and implement in other class like ModelSerializer
class inheritance Serializer
and if we overwrite this method in our serializer class and re fetch data before send to view layer, implement like:
from collections import OrderedDict
class OurSerializer(serializer.ModelSerializer):
....
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['message'] = 'Done'
result['status'] = 'sucssed'
return result
this solution solve above problem but we have two problem again
one: if we use nested serializer and we had overwrite this function in serializer class return bad data like:
{
'data': {
'value_one': 'some data',
'value_two': 'some data',
'value_three': {
'data': [
'value', 'value', 'value'
],
'message': 'Done',
'status': 'sucssed',
},
}
'message': 'Done',
'status': 'sucssed',
}
and message
and status
repeated and structure not pretty for client
and two: we cant handle exception in this mode and just way to handle exception just with middleware class like this DRF Exception Handling and this isn't useful way, we can't handle any type of error occurs in view and generate comfortable separate message
and status
.
IF there's another good solution to this question, please guide me.
thanks :)
回答1:
To resolve this, best practice (that DRF has proposed) is to use 'renderer' classes. A renderer manipulates and returns structured response.
Django uses renderers like Template Renderer and DRF benefits this feature and provides API Renderers.
To do so, you could provide such this renderer in a package (e.g. app_name.renderers.ApiRenderer
):
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import json
class ApiRenderer(BaseRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
response_dict = {
'status': 'failure',
'data': {},
'message': '',
}
if data.get('data'):
response_dict['data'] = data.get('data')
if data.get('status'):
response_dict['status'] = data.get('status')
if data.get('message'):
response_dict['message'] = data.get('message')
data = response_dict
return json.dumps(data)
And then in your settings file:
REST_FRAMEWORK = {
...
'DEFAULT_RENDERER_CLASSES': (
'app_name.renderers.ApiRenderer',
),
...
}
By this action all views that extend DRF generic views will use renderer. If you needed to override setting you can use renderer_classes
attribute for generic view classes and @renderer_classes
decorator for api view functions.
A comprehensive renderer class to override is available at <virtualenv_dir>/lib/python3.6/site-packages/rest_framework/renderers.py
.
回答2:
Did you try to write custom Response middleware:
class ResponseCustomMiddleware(MiddlewareMixin):
def __init__(self, *args, **kwargs):
super(ResponseCustomMiddleware, self).__init__(*args, **kwargs)
def process_template_response(self, request, response):
if not response.is_rendered and isinstance(response, Response):
if isinstance(response.data, dict):
message = response.data.get('message', 'Some error occurred')
if 'data' not in response.data:
response.data = {'data': response.data}
response.data.setdefault('message', message)
# you can add you logic for checking in status code is 2** or 4**.
data_status = 'unknown'
if response.status_code // 100 == 2:
data_status = 'success'
elif response.status_code // 100 == 4:
data_status = 'failure'
response.data.setdefault('data_status', data_status)
return response
Add middleware in settings:
MIDDLEWARE = [
# you all middleware here,
'common.middleware.ResponseCustomMiddleware',
]
So you can return Response
like this:
data = {'var1': 1, 'var2': 2}
return Response({'data': data, 'message': 'This is my message'}, status=status.HTTP_201_CREATED)
Response will be like:
{
"data": [
{
"var1": 1,
"var2": 2
}
],
"message": "This is my message",
"data_status": "success"
}
回答3:
This is how I solve the problem. I hope it helps
def custom_response(data, code=None, message=None):
if not code and not message:
code = SUCCESSFUL_CODE
message = SUCCESSFUL_MESSAGE
return Response(OrderedDict([
('code', code),
('message', message),
('results', data)
]))
Now in your views function. You can custom the response however you want pretty easy return custom_response(data=..,message=...,code=...)
来源:https://stackoverflow.com/questions/53910545/how-overwrite-response-class-in-django-rest-framework-drf