【応用編】Django REST framework (DRF)チュートリアル-Slack風サービスを開発する-

NO IMAGE

まだまだ途中ですが、ひとまず公開します。。。

前回は以下の記事でDRFの基本的な開発についての記事を作成しました。

ということで今回は、実際に開発をしていくなかで出逢うであろう壁とそれを乗り越えるためのTips を共有していこうと思います!
また、drfの全体の大まかな流れは以下になります。長いので別記事にして投稿予定です。
【設計編】開発するものの概要や簡単な設計書の紹介
【基本編】簡単なCRUDのAPIを作ってみる(本記事)
【応用編】ちょっと特殊な部分を説明(本記事)
【TEST編】DRFのTESTについて
【CI編】Circle ci , Heroku でサーバーにあげてみる
【Front編】vue.jsで実際にAPIのbindingをしてみる

ちょっと振り返り

記事の作成に時期が空いてしまったので振り返らせてください( ´Д`)y━・~~

。。。。。。
はい笑

基本編では割とシンプルなThread のCRUDに対するAPIを作成しました。

今回は本格的に以前作成したAPIの一覧を開発しに行きます!!

URLMethod説明
/v1/threads/POSTthreadの作成
/v1/threads/GETthread一覧の参照
/v1/threads/1/GETthreadの詳細表示 pageのAPIで対応
/v1/threads/1/PATCHthreadの編集
/v1/threads/1/DELETEthreadの削除
/v1/threads/?q=aGETaをタイトルに含むThreadの検索機能
/v1/thread_members/POSTmemberを追加 複数もあり
/v1/thread_members/GET特定のThreadのmemberのみ見えればいいので実装しない
/v1/thread_members/1GETこれは不要
/v1/thread_members/1/PATCH参加するかしないかなのでPOST,DELETEだけでいいのでPATCHは実装しない
/v1/thread_members/1/DELETEthreadを退会したり、退会させたりする時に使う
/v1/thread_members/?thread=1GET特定のthread memberの一覧表示
/v1/comments/POSTCommentの作成
/v1/comments/GETprivateなThreadのコメントなどは見えたらダメなので、?thread=1などFilterは実装しますがこちらは実装しません。
/v1/comments/1/GETこちらは不要です。
/v1/comments/1/PATCHコメントの編集
/v1/comments/1/DELETEコメントの削除
/v1/comments/?thread=1GET特定のthreadのコメント一覧表示
/v1/page_thread/GETThreadをクリックしたときのAPIです。Thread memberの一覧とコメントの一覧を表示させます。
/v1/users/GET登録ユーザー一覧 ThreadのMemberを追加する時に使う
/v1/auth/login/google-oauth2POSTLogin,Register はGoogleアカウントを使う予定なのでここはsocial-auth-app-djangoというpackageに任せます。

CRUDの処理のオーバーライド

まず、一番上の /v1/threads/ (POST) … threadの作成について考えます。

前回も一応APIは作成しました。

今回は前回に追加して、threadを作成する時にThreadMemberのTableにもそのthreadを作った人を登録するという処理を入れてみたいと思います。(今後様々なところで「threadのmemberだったらOOという処理をしてもいい。」みたいな分岐が多くあるので、その時にOwnerも含まれるようにthreadを作成時にthreadmemberにもデータをInsertします。)

ちなみにThreadMember Tableの定義はこちらになります。(migrationなど忘れないでくださいね!)

さて、前置きが多くなりましたが、「createの処理をdrfのdefaultで作ったものではなくて独自で処理を書き換えたい」、つまり「CRUDの処理をオーバーライドする」にはどうすれば良いのか。

早速ですが以下で今回実現したいことは達成できます。

class ThreadViewSet(ThreadPermission, viewsets.ModelViewSet):
    """
    Thread CRUD, only owner can patch and delete thread.
    """

    def get_queryset(self):
        if self.request.method == 'GET':
            return models.Thread.objects.all().filter(
                is_public=True).order_by('title')
        return models.Thread.objects.all()
    serializer_class = serializer.ThreadSerializer

    def perform_create(self, serializer):
        created_data = serializer.save()
        models.ThreadMember(thread=created_data,
                            user=created_data.owner).save()

perform_create を追加しました。

詳細は公式サイトにもありますが、簡単に説明すると

perform_create,perform_update , perform_destroy を使うことでCRUDのR以外の処理をオーバーライドすることができます。

Readがここで用意されていないのはserializerで対応できるからだと思われます。

ちなみにperform_createはCreateの処理を上書きするので呼ばれるタイミングはCreateする直前です。また、peform_create内でdbへのsaveを行わない場合はPOSTで投げたのにdataがDBに入らないという事態になってしまうのでご注意ください。

def perform_create(self, serializer):
    created_data = serializer.save()
    models.ThreadMember(thread=created_data,
                        user=created_data.owner).save()

perform_create内の説明ですが、まず引数のselfは今まで通りclass自身ですので、self.request などを使うことができます。

そして、serializerですが、こちらをsaveすることで今まで行っていた、URLに対するリソースのInsertが行えます。

そしてsave()の返り値がInsertされたmodelになっています。ここではThreadです。

今回は追加の処理をしたいので、serializer.save()の後に、ThreadMemberへのdataのInsertを行っています。

ちなみに、追加の処理をしたい場合だけではなく、こんな使い方もできます。

def perform_create(self, serializer):
created_data = serializer.save(owner=self.request.user)

これはrequestのパラメータによらずに、DBへの追加時にカラムの値を固定することができるということです。

例えば、上のようにSecurity的に改竄がされないように使ったり、DBの都合上default値を変更することができないが、初期値を固定したい場合などに使えると思います。