web-dev-qa-db-ja.com

Django RESTフレームワーク(DRF)のResponseクラスをどのように上書きしますか?

Django RESTフレームワークのResponseクラスを上書きして、応答ディクショナリディクショナリに3つのパラメーターmessagestatusおよびdata

こんにちは親愛なる

DRFの_Response Class_を変更して、2つの追加パラメーター(メッセージ、ステータス)とDRFシリアライザーによって提供されるデータを渡すようにしています。 messageDone、_User Created_などのメッセージを渡し、statusfailまたはsuccessなどのメッセージを渡しますメッセージとこのメッセージは、フロントエンドとバックエンド間の特別なコードを予約するのに役立ちます。

このパラメーターを設定しない場合は、空の文字またはnullの結果をクライアント側に返します

例えば成功モードで:

_{
    'data': {
        'value_one': 'some data',
        'value_two': 'some data',
        'value_three': [
                'value', 'value', 'value'
            ],
        },
    }
    'message': 'Done',
    'status': 'success',
}
_

失敗モードでは:

_{
    'data': ['any error message raise by serializer',]
    'message': 'Create User Failed',
    'status': 'failure',
}
_

私は自分の質問について検索し、この解決策を見つけました:

クラスで_DRF Response Class_を継承して___init___メソッドを上書きし、このメソッドでメッセージ、データ、ステータスを取得して、独自のデータ構造を持つ親のinitを呼び出し、このレスポンシブクラスを使用する場合このような私の機能では実装:

_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
        )
_

成功モードの呼び出し:

_return Response(data=serializer.data, message='Done', data_status='success', status=200)
_

失敗モードの呼び出し:

_return Response(data=serializer.errors, message='Create User Failed', data_status='failure', status=400)
_

このソリューションでproblemを使用したすべてのビュークラスで独自のResponseクラスを使用します。_GenericViews Class_を使用する場合、ビューのロジックで使用したすべてのhttpメソッドを上書きし、独自のクラスを呼び出す必要があります乾燥!!


そして私が見つけた他の解決策。シリアル化された層では、Serializerクラスに抽象メソッドdef to_representation(self, instance):があり、ModelSerializerクラス継承Serializerなどの他のクラスに実装し、このメソッドをシリアライザクラスを使用して、ビューレイヤに送信する前にデータを再フェッチし、次のように実装します。

_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
_

この解決策は上記の問題を解決しますが、再び2つの問題があります

1つ目:入れ子のシリアライザーを使用していて、シリアライザークラスでこの関数を上書きした場合、次のような不良データが返されます。

_{
    'data': {
        'value_one': 'some data',
        'value_two': 'some data',
        'value_three': {
            'data': [
                'value', 'value', 'value'
            ],
            'message': 'Done',
            'status': 'sucssed',
        },
    }
    'message': 'Done',
    'status': 'sucssed',
}
_

およびmessagestatusが繰り返され、構造がクライアントに不適切です

2つ目:このモードでは例外を処理できず、このようなミドルウェアクラスだけで例外を処理する方法 DRF Exception Handling であり、これは役に立たない方法であり、発生したエラーのタイプを処理できませんビューで快適な個別のmessagestatusを生成します。

この質問に別の良い解決策がある場合は、私を案内してください。

ありがとう:)

7
Erfan

これを解決するためのベストプラクティス(DRFが提案した)は、「レンダラー」クラスを使用することです。レンダラーは構造化された応答を操作して返します。

Djangoは テンプレートレンダラー のようなレンダラーを使用し、DRFはこの機能にメリットをもたらし、 APIレンダラー を提供します。

これを行うには、このようなレンダラーをパッケージで提供できます(例: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)

そして、設定ファイルで:

REST_FRAMEWORK = {
    ...
    'DEFAULT_RENDERER_CLASSES': (
        'app_name.renderers.ApiRenderer',
    ),
    ...
}

このアクションにより、DRF汎用ビューを拡張するすべてのビューがレンダラーを使用します。設定をオーバーライドする必要がある場合は、ジェネリックビュークラスにはrenderer_classes属性を、APIビュー関数には@renderer_classesデコレータを使用できます。

オーバーライドする包括的なレンダラークラスは、<virtualenv_dir>/lib/python3.6/site-packages/rest_framework/renderers.pyにあります。

4
Ali Farhoudi

カスタムレスポンスミドルウェアを作成しようとしましたか?

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

設定にミドルウェアを追加します。

MIDDLEWARE = [
    # you all middleware here,
    'common.middleware.ResponseCustomMiddleware',
]

したがって、次のようにResponseを返すことができます。

data = {'var1': 1, 'var2': 2}
return Response({'data': data, 'message': 'This is my message'}, status=status.HTTP_201_CREATED)

応答は次のようになります。

{
  "data": [
    {
        "var1": 1,
        "var2": 2
    }
  ],
  "message": "This is my message",
  "data_status": "success"
}
1
Sergey Pugach

これが私が問題を解決する方法です。それが役に立てば幸い

_    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)
    ]))
_

これでビュー関数になりました。非常に簡単にしたいが、レスポンスをカスタマイズできますreturn custom_response(data=..,message=...,code=...)

0
Quang Võ