web-dev-qa-db-ja.com

GETパラメータをPOST Django REST Framework

内部ウェブサイトのバックエンドをPHP to Django(using REST framework)から)に書き換えているところです。

両方のバージョン(PHPとDjango)はしばらくの間同時にデプロイする必要があり、シンプルなAJAX APIを介してレガシーWebサイトと対話する一連のソフトウェアツールがあります。すべてのリクエストはGETメソッド。

これまでのところ、両方のサイトでリクエストを機能させるためのアプローチは、Ajaxコントローラーへの呼び出しをシミュレートするために 'http://<site-name>/ajax.php'にルーティングされた単純なアダプターアプリを作成することでした。上記のアプリには、着信リクエストからデータを取得して、対応するDjangoビューで着信リクエストを呼び出すビュー(基本的にはAjaxコントローラーがPHPバージョン)。

動作しますが、問題が発生しました。私のAPIアクションの1つは、DBテーブルでの単純なエントリの作成でした。そこで、いくつかの一般的なミックスインを使用してDRFビューセットを定義しました。

class MyViewSet(MyGenericViewSet, CreateModelMixin):
    # ...

これにより、ページ上のcreateリクエストにルーティングされるPOSTアクションが追加されます。まさに私が必要とするもの。着信リクエストがGETメソッドを使用していることを除いて...独自のcreateアクションを記述してGETリクエストを受け入れるようにすることができますが、長期的には、ツールが適応しますDjango APIとアダプターアプリは不要になるため、「クリーン」なビューセットとモデルを用意します。そのような場合はPOSTを使用するほうが理にかなっています。アクション。

私のアダプターアプリビューで、私は単純にこれを試しました:

request.method = "POST"
request.POST = request.GET

リクエストをcreateビューに渡す前。期待どおりに機能せず、CSRF認証失敗メッセージが表示されましたが、アダプターアプリビューには@csrf_exemptデコレーターがあります...

私はここで三角形を正方形に収めようとしているかもしれませんが、自分のcreateアクションを書き直さずにこれを機能させる方法はありますか?

4
Valentin B.

すべての回答からのアドバイスが別のビューを作成することを指しているので、これが私がやったことです。内部adapter/views.py

from rest_framework.settings import api_settings
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.response import Response
from rest_framework import status

from mycoreapp.renderers import MyJSONRenderer
from myapp.views import MyViewSet

@api_view(http_method_names=["GET"])
@renderer_classes((MyJSONRenderer,))
def create_entity_from_get(request, *args, **kwargs):
    """This view exists for compatibility with the old API only. 
    Use 'POST' method directly to create a new entity."""
    query_params_copy = request.query_params.copy()
    # This is just some adjustments to make the legacy request params work with the serializer
    query_params_copy["foo"] = {"name": request.query_params.get("foo", None)}
    query_params_copy["bar"] = {"name": request.query_params.get("bar", None)}
    serializer = MyViewSet.serializer_class(data=query_params_copy)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    try:
        headers = {'Location': str(serializer.data[api_settings.URL_FIELD_NAME])}
    except (TypeError, KeyError):
        headers = {}
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

もちろん、私は自分のプロジェクトに固有のすべての名前を難読化しています。基本的に私はDRFミックスインのcreateperform_createおよびget_success_headerメソッドで何が起こるかをほぼ正確に再現しました(クエリパラメータの微調整を除いて)CreateModelMixin単一機能ベースのDRFビュー。スタンドアロン関数であるため、adapterアプリビューに配置できるため、すべてのレガシーAPIコードが1か所だけに配置されます。これがこの質問の意図です。

1
Valentin B.

createリクエストを受け入れて実行できる @action デコレータを利用することで、元のメソッドを上書きせずに、ビューセットでカスタムGETメソッドを定義できます。作成:

class MyViewSet(MyGenericViewSet, CreateModelMixin):
    ...
    @action(methods=['get'], detail=False, url_path='create-from-get')
    def create_from_get(self, request, *args, **kwargs):
        # Do your object creation here.

Router in your urls を使用してactionを自動的にURLに接続する必要があります(SimpleRouterがほとんどの場合)。
あなたのurls.py

router = SimpleRouter()
router.register('something', MyViewSet, base_name='something')

urlpatterns = [
    ...
    path('my_api/', include(router.urls)),
    ...
]

これで、actionリクエストからモデルインスタンスを作成できるGETができました(ただし、その作成を行うロジックを追加する必要があります)。次のURLでアクセスできます。

your_domain/my_api/something/create-from-get

このエンドポイントが不要になった場合は、コードのこの部分を削除するだけで、アクションが存在するようになります(または、レガシーな理由で保持することもできます)。

1
John Moutafis

RESTのアーキテクチャの原則に従って、メソッドGETは情報を取得することのみを目的としています。そのため、リクエストメソッドcreateGET操作を実行しないでください。 create操作を実行するには、リクエストメソッドPOSTを使用します。

あなたの質問に対する一時的な修正

from rest_framework import generics, status

class CreateAPIView(generics.CreateView):

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(
            serializer.data,
            status=status.HTTP_201_CREATED,
            headers=headers)

    def get(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

詳細については、以下のリファレンスを参照してください。
https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
https://learnbatta.com/blog/introduction-to-restful-apis-72/

0

ビューセットのメソッドを書くことができます(custom_get)これは、URLに対してGET呼び出しが行われたときに呼び出され、そこからcreateメソッドを呼び出します。

class MyViewSet(MyGenericViewSet, CreateModelMixin):
    ...
    def custom_get(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

そしてあなたのurls.py、ビューセットでは、このメソッドをGET呼び出しで呼び出す必要があることを定義できます。

#urls.py
urlpatterns = [
    ...
    url(r'^your-url/$', MyViewSet.as_view({'get': 'custom_get'}), name='url-name'),
]

0
Aman Garg