MENU

Django Ninjaチュートリアル11(レスポンスの変更)

Django-Ninjaチュートリアル11(レスポンスの変更)

前回までの記事はこちらです。

前回までの記事が読まれている前提で書いていきますので、まだ読んでいない方はこちらから読み進めて追いついてください。

それでは始めていきましょう。

目次

レスポンスの変更

今回参考にする公式ドキュメントはこちらです。

Webアプリケーションを作る場合、ヘッダーを追加したり、Cookieを変更したりといった、レスポンスが提供される直前にレスポンスを変更する場合が想定されます。
これに対応するには、HttpResponseタイプの関数パラメーターを宣言するだけで対応することができます。

APIを設定していきます。

from ninja import NinjaAPI

from django.http import HttpRequest, HttpResponse

app_v5 = NinjaAPI(
  title = "Django-Ninja Sample App5",
  version = "1.0.5",
)

@app_v5.get("/cookie/")
def feed_cookiemonster(request: HttpRequest, response: HttpResponse):
  response.set_cookie("cookie", "delicious") # cookieを設定
  response["X-Cookiemonster"] = "blue" # headerを設定
  return {"cookiemonster_happy": True}

上記のように、GETメソッド実行時に、再度cookieを設定し、headerを再度設定することができます。

Temporal response object

このレスポンスオブジェクトは、エラーに対するレスポンスを含め、Django Ninjaにより作成されたすべてのレスポンスのベースとして使用されるものです。
DjangoのHttpResponseオブジェクトが操作によって直接返される場合、Temporal response objectは使用されません。

明らかに、このレスポンスオブジェクトにはコンテンツが含まれていないものになりますが、content_typeは含まれています。

status_codeは戻り値によってオーバーライドされます(デフォルトではステータスコードは200、または2つの要素を含んでいるタプルが返される)。

メソッドをオーバーライドしてTemporal response objectを変更する例は以下のとおりです。
この場合、NinjaAPI.create_temporal_responseを使用します。

def create_temporal_response(self, request: HttpRequest) -> HttpResponse:
  response = super().create_temporal_response(request)
  return response

実際の使用例は、ここでは省略します。

DjangoのモデルからSchemaを生成する

Schemaは、バリデーションのルールとレスポンスを定義するために役立ちますが、データベースモデルをSchemaに反映し、変更を同期させる必要がある場合もあります。
Django Ninjaでは、これに対応するため、Djangoで定義したモデルに対応したSchemaを生成できる仕組みが用意されています。

Model Schema

ModelSchemaは、Djangoで定義したモデルからSchemaを自動的に生成できる特別なベースクラスです。
必要な設定は、Djangoのモデルを設定し、Schema Configmodel_fields属性を設定するのみです。

モデルに設定したFieldを個別に指定する場合

Djangoのモデルで設定した各Fieldを個別に設定する場合は、以下のように設定します。

from register.models import User
from ninja import ModelSchema

class UserSchema(ModelSchema):
  class Config:
    model = User
    model_field = ['id', 'username']

model_fieldに各Field名を設定します。

全てのFieldを指定する場合

Djangoのモデルで設定したFieldを全て指定する場合は、以下のように書きます。

from register.models import User
from ninja import ModelSchema

class UserSchema(ModelSchema):
  class Config:
    model = User
    model_field = "__all__"

model_field"__all__"を指定します(この書き方はDjango REST FrameworkでSerializerを書くときと同じです)。

"__all__"を使用することは推奨されていません。
というのも、これを設定することにより、偶発的な望ましくないデータの漏洩が発生する可能性があります(例えば、ハッシュ化されたパスワードなどが漏洩するなど)。
model_fieldsで使用したいモデルのFieldを明示的に定義する方法が推奨されています。

一部のFieldを除外したいとき

Djangoで設定したモデルのFieldの一部を除外してSchemaを設定したい時は、以下のように書きます。

from register.models import User
from ninja import ModelSchema

class UserSchema(ModelSchema):
  class Config:
    model = User
    model_field = ['id', 'username']
    model_exclude = ['password', 'last_login', 'user_permissions']

model_excludeに除外したいFieldを記述します。

ログインユーザーを取得してみる

過去に作ったAPIに作成したModelSchemaで作成したSchemaを適用し、APIが問題なく使えるかを確認します。

以下のように、Schemaに追記してください。

from register.models import User
from ninja import ModelSchema, Schema # add

class UserSchema(ModelSchema):
  class Config:
    model = User
    model_field = ['id', 'username']
    model_exclude = ['password', 'last_login', 'user_permissions']

# add
class ErrorSchema(Schema):
  message: str

過去に作成したErrorSchemaを使用します。
こちらは通常のSchemaを継承しています。

次にAPIを作成します。

from ninja import NinjaAPI

from example.Schema.api_v5_schema import UserSchema, ErrorSchema

app_v5 = NinjaAPI(
  title = "Django-Ninja Sample App5",
  version = "1.0.5",
)

@app_v5.get("/me", response = {200: UserSchema, 403: ErrorSchema})
def get_me(request):
  if not request.user.is_authenticated:
    return 403, {"message": "First, please sign in..."}
  return request.user

ログインしているユーザーを返すAPIです。
これのresponseにSchemaを結びつけています。

実行結果を確認すると、ログインユーザー(おそらく作成したsuperuser)の情報が返されると思います。
model_excludeを指定しているため、password等はレスポンスには含まれていないことを確認してください。

今回はSuperuserの情報が返ってくるため、私の情報が出てしまいます。
実行結果は各自でご確認くださいm(_ _)m

このように、Djangoで作ったモデルは、データベースに紐づいたモデルであることに加え、Django NinjaにおけるSchemaにも転用できてしまいます。
これまで作成してきたように手動でSchemaを作ることもできますが、Djangoでデータベースに紐づくモデルを作成することで、このモデルに連動したSchemaを自動で作れることになります。
便利な機能ですね!!!

Fieldのオーバーライド

Djangoのモデルに連動して、ModelSchemaを使用したSchema設定ができることがわかりましたが、場合によっては、一部のフィールドのデフォルトのアノテーションを変更したり、新しいフィールドを追加したい場合が出てきます。
この場合は、通常どおりSchemaを設定することができます。
例を見てみます。

class GroupSchema(ModelSchema):
    class Config:
        model = Group
        model_fields = ['id', 'name']


class UserSchema(ModelSchema):
    groups: List[GroupSchema] = []

    class Config:
        model = User
        model_fields = ['id', 'username']

GroupSchemaは、Djangoのモデルに紐づくモデルです。
ModelSchemaを使用して、DjangoのモデルからSchemaを生成しています。

UserSchemaは、groupsというUserモデルでも持っている要素について先に設定したGroupSchemaをList形式で型定義しています。

このように、UserSchemaに元々あるgroupsというFieldを個別に設定したSchemaで上書きすることができます。

ここでは、groupsとして返される値をmodel_fieldsによりidnameに限定しています。
また、UserSchemaでは、groupsはリスト形式で扱われるため、デフォルト値で[](空リスト)を設定しています。

最後に

今回は、レスポンスの変更とModelSchemaを使用したSchema設定の方法を確認しました。
ModelSchemaを利用して、Djangoモデルに関連づけたSchema設定は、目から鱗でした。
基本として手作業のSchema設定を見てきましたが、こんなに便利で効率的で確実なSchema設定があるとは!といった感じです。

Djangoベースであることからできる所業なのでしょう。

次回は、動的なSchemaの生成を見ていきたいと思います。

また、次回もよろしくお願いします。

Django-Ninjaチュートリアル11(レスポンスの変更)

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

はじめまして、ふじです。
Python、Django、FastAPI、React.js、Next.jsを学習している、ずっと文系のプログラミング独学者です。

目次