リクエストメソッド

リクエストメソッド

クラスビューでは、リクエストメソッドの種類に応じて、処理する関数を切り替えることができます。
DjangoではCRUDの処理に従ってクラス内でそれぞれ別の関数を用意することができます。

  • Create
    def post
  • Read
    def get
  • Update
    def put
  • Delete
    def delete ※DjangoではDeleteViewと併用する

POSTリクエスト

HTMLのFormを使ったPOSTリクエストであれ、ajaxのPOSTリクエストであれ、axiosのPOSTリクエストであれ、全てのPOSTリクエストは、post関数で受け取ることができます。

前章で扱ったどの汎用クラスビューを用いても、post関数は設置することができます。
ただし、そのリクエストがFormリクエストなのか、非同期リクエスト(ajax, axios)なのかで処理が変わってくるので注意しましょう。

FormのPOSTリクエスト

FormのPOSTリクエストの場合は、メッセージを加えてページ遷移させるのが一般的です。
Djangoには、メッセージフレームワークというものが標準で付いていて、ページ遷移の際に、一緒に文字列のデータを遷移先に送信することができます。
それにより、ユーザーはリクエストが成功したかどうかを簡単に判断することができます。

views.py

from django.views.generic import TemplateView
from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponseBadRequest

class IndexView(TemplateView):

    template_name = 'index.html'

    def post(self, request, *args, **kwargs):
        post = request.POST

        # request_postというnameのデータを送信したと仮定する
        if post.get('request_post'):
            print('Accepted Post Request')
            messages.success(request, 'POST処理を実行しました。')
            incoming_url = request.META['HTTP_REFERER']
            return HttpResponseRedirect(incoming_url)

        return HttpResponseBadRequest()

index = IndexView.as_view()

request.POST
POSTで送信されるデータは、request.POSTで受け取ることができます。
データは辞書型になっているので、get()をすることでその中身を取り出すことができます。もし値が存在しない場合はNoneが返るので、そこで分岐をかけることができます。

messages.success
こちらがメッセージフレームワークです。今回は、successという種類のメッセージを使用しています。
基本的な書き方は以下のようにします。
messages.種類(request, メッセージの内容)

またメッセージの種類はこんな感じです。

messages.success
messages.info
messages.warning
messages.error

メッセージは複数送られることもあり、配列に似た形で遷移先に送られます。
HTMLでは、以下のような形でデータを受け取って表示することができます。

index.html


{% if messages %}
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %} py-3 mt-1">
    <div class="container">
        <div class="alert-icon">
            <i class="material-icons">announcement</i>
        </div>
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true"><i class="fa fa-times"></i></span>
        </button>
        <span class="ml-3">{{ message }}</span>
    </div>
</div>
{% endfor %}
{% endif %}


上記はBootstrap4も併用していますが、messagesをfor文で回して、種類に応じたクラス名を振っています。
そのクラス名に応じて、色やスタイルを変更したりします。

request.META
Djangoでは、ほぼ全てのバックエンドにrequestが存在し、そのrequestから様々なデータを取得することができます。
気になる方は、print(request.META)をしてみると良いでしょう。
今回は、request.METAから HTTP_REFERERを取得しています。

これによりリクエスト元のURLを取得することができ、POSTリクエストの処理を終えてからリクエスト元のURLに遷移させています。

HttpResponseRedirect
HttpResponseRedirectを使用すると、URLパスを指定することでリダイレクトをかけることができます。

HttpResponseBadRequest
もしrequest_postというデータが存在しない場合は、ステータスコード400番のBad Requestを返しています。
Djangoには他にもHttp404などステータスコードに応じたリクエストへのレスポンス機能が用意されているので、調べてみると良いでしょう。

以上がFormリクエストに対する一般的なレスポンスの記述になります。
基本的なフローは以下のようになるでしょう。

request.POSTでデータを受け取る
処理を記述する
次のページへ遷移させる

非同期リクエスト(ajax, axios)

DjangoのViewをAPIライクに使用する場合は、非同期リクエストが使われます。
Formリクエストのように、ページ遷移させるのではなくjsonのデータ形式にしてデータを返すのが一般的です。
先ほどすでに上述したようにリクエストで送られるデータは受け取ることができます。

ajaxの場合は、Formリクエストと同じでrequest.POSTでPOSTデータを受け取ることができますが、axiosの場合は、request.body内にデータが格納されています。
axiosの場合データはjson化されているので、読み込む必要があります。下記のようにデータを受け取ることができます。

json.loads(request.body)

受け取ったデータは最終的に辞書型のデータにして、json形式でレスポンスします。
下記が一例です。

views.py

import json

from django.views import View
from django.http import HttpResponseBadRequest
from django.http.response import JsonResponse

class IndexAPIView(View):

    def post(self, request, *args, **kwargs):
        # ajaxの場合
        post = request.POST
        # axiosの場合
        post = json.loads(request.body)

        if post.get('request_post'):
            accepted = 'Accepted Post Request'
            response = {
                'accepted': accepted
            }
            return JsonResponse(response)

        return HttpResponseBadRequest()

index_api = IndexAPIView.as_view()


View
APIとしてViewを扱う場合は、該当するtemplateのHTMLファイルは存在しないので、Viewを使います。Viewを使ってgetやpostリクエストの処理を記述します。

JsonResponse
JsonResponseを使うとjson形式でデータを返してくれます。
辞書型のデータを入れると自動的にjson形式にしてデータをレスポンスしてくれます。
返すデータが辞書型ではない場合、続く引数に safe=Trueを指定して、JsonResponse(response, safe=True)のような形にしましょう。

Djangoには、REST Framework というライブラリが存在しそちらを組み込むことでさらに最適なAPI実装を行うことができますが、Djangoの既存機能を使用してもAPIの作成はすることができます。Djangoの一般的な実装の域を超えずにAPIを実装できるので便利です。
ケースバイケースで上述のような形で内部的なAPIを実装していけると良いでしょう。

GETリクエスト

ViewでGETリクエストを扱うケースも多々あります。
例えばユーザーがページをロードするときもGETリクエストが走ります。
FormのGETリクエストの際もGETリクエストが走ります。

views.py

from django.views.generic import TemplateView

class IndexView(TemplateView):

    template_name = 'index.html'

    def get(self, request, *args, **kwargs):
        # FormのGETリクエストのパラメーターは以下のようにして取得できる。
        name = request.GET.get('name')

        print(name)
        return super().get(request, **kwargs)

index = IndexView.as_view()

getをただオーバーライドして処理を各ケースは少ないと思います。
上記では、FormのGETリクエストなどの際にパラメーターをURLから取得しています。
例えばURLが以下のような場合

/hoge?name=test

name = request.GET.get('name')を記述することで testを取得することができます。

また既に触れたget_context_dataではフロントエンドにデータを投げる処理について記述しました。
実はこのget_context_dataは、getにてオーバーライドしてフロントエンドにデータを投げることができます。

views.py

from django.views.generic import TemplateView

class IndexView(TemplateView):

    template_name = 'index.html'

    def get(self, request, *args, **kwargs):
        get = super().get(request, **kwargs)
        context = get.context_data

        username = request.user.username
        context['username'] = username

        return get

index = IndexView.as_view()

こちらでは、getの処理の中でまずcontext_dataを取得しています。
context_dataには、バックエンドからフロントエンドに投げられるデータが辞書型で入っています。

ここではこのcontext_dataにusernameを格納しています。
こうすることでフロントエンドでは、{{ username }}と記述することでバックエンドで取得したユーザーのユーザー名を表示することができます。

getでget_context_dataの処理を使用するケースは主に1つです。

クラスビューにget_context_dataを記述してgetも記述する場合、もしかしたら同じ処理を記述してしまうかもしれません。
同じ関数内で処理を記述できれば、記述するコードは同じものを使用できますしそもそもデータベースへのアクセスは最小限にするべきです。
同じ処理を書く必要が出てくる場合、getのなかでcontext_dataを使用すると良いでしょう。

PUTリクエスト

最後にPUTリクエストについて触れていきます。
PUTリクエストとは、更新処理のためのリクエストです。
GETは主にデータ取得のために使用され、
POSTはデータ作成のために使用されます。
PUTはデータを更新する際に使用されます。

views.py

import json

from django.views import View
from django.http import HttpResponseBadRequest
from django.http.response import JsonResponse

class IndexAPIView(View):

    def put(self, request, *args, **kwargs):
        # ajaxの場合
        put = request.PUT
        # axiosの場合はPOSTと同じ
        post = json.loads(request.body)

        if post.get('request_post'):
            updated = 'Accepted Put Request'
            response = {
                updated: updated
            }
            return JsonResponse(response)

        return HttpResponseBadRequest()

index_api = IndexAPIView.as_view()

PUTリクエストでは、def putを記述することで処理を記述することができます。
実際に使用される場合APIとしての使用が多いのでAPIViewとして記述しています。

またaxiosを使用する場合はrequest.bodyにデータが格納されるので、たとえPUTリクエストでもデータの受け取りにはjson.loads(request.body)を使用します。

ここではPUTリクエストについて触れましたが、実際にはPOSTリクエストとして扱うケースがほとんどです。
putであってもpostであっても実際に実装する仕様に違いはほとんどありません。

データの更新処理などでもPOSTリクエストで処理を記述するケースが多いのでputを使用するケースはあまりありません。
ただしclassが肥大化してしまう場合は、データベース処理ごとに関数を切り分けることができるので好みに応じて使い分けてください。

リクエストを理解する

クラスビューはリクエストの扱いを理解することで実装の幅が一気に広がります。
フロントエンドと合わせて、Formリクエストや非同期リクエスト、GETやPOST、これらを使いこなすことで大抵の機能は実装することができます。