内部ウェブサイトのバックエンドを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
アクションを書き直さずにこれを機能させる方法はありますか?
すべての回答からのアドバイスが別のビューを作成することを指しているので、これが私がやったことです。内部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ミックスインのcreate
、perform_create
およびget_success_header
メソッドで何が起こるかをほぼ正確に再現しました(クエリパラメータの微調整を除いて)CreateModelMixin
単一機能ベースのDRFビュー。スタンドアロン関数であるため、adapter
アプリビューに配置できるため、すべてのレガシーAPIコードが1か所だけに配置されます。これがこの質問の意図です。
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
このエンドポイントが不要になった場合は、コードのこの部分を削除するだけで、アクションが存在するようになります(または、レガシーな理由で保持することもできます)。
REST
のアーキテクチャの原則に従って、メソッドGET
は情報を取得することのみを目的としています。そのため、リクエストメソッドcreate
でGET
操作を実行しないでください。 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/
ビューセットのメソッドを書くことができます(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'),
]