MENU

Django Ninjaチュートリアル12(動的なSchemaの設定)

Django Ninjaチュートリアル12(動的なSchemaの設定)

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

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

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

目次

動的なSchemaの設定

今回取り扱うチュートリアルの内容は、動的なSchemaの設定です。

Django Ninjaでは、内部的にModelSchemaを使用して、create_schemaを実行しています。

ただし、上記の方法は安全性が低く、使用の際は注意が必要です。

ヘルパー関数

Django Ninjaでは、create_schema関数において、ヘルパー関数(いわゆるDjango Ninjaの独自関数)が設定されています。
例えば以下に示すようなものです。

def create_schema(
    model, # Djangoのモデル
    name = "", # 生成されたクラスの名前(ただし、空のモデルについては、モデル名が使用される)
    depth = 0, # Schemaが0以上の場合、ForeignKeys and ManyToMany をネストします
    fields: list[str] = None, # 値が渡された場合のみSchemaに追加される
    exclude: list[str] = None, # 値が渡された場合のみSchemaから除外される
    custom_fields: list[tuple(str, Any, Any)] = None, # 値が渡された場合のみ、デフォルトのフィールドの型をオーバーライドする(もしくは新しいフィールドを追加する)
)

例を見てみます。
以下のようにDjangoの標準モデルのUserからcreate_schemaによりUserSchemaを生成したとします。

from django.contrib.auth.models import User
from ninja.orm import create_schema

UserSchema = create_schema(User)

すると、この結果生成されるSchemaは以下のようになります。

class UserSchema(Schema):
  id: int
  username: str
  first_name: str
  last_name: str
  password: str
  last_login: datetime
  is_superuser: bool
  email: str
  ... and the rest

このようにModelSchema(内部的にはcreate_schema)により生成されたSchemaはDjangoで設定したクラスが持つすべてのフィールドについて、Schemaを自動生成します。

ここで注意が必要なのは、passwordなどの、通常は非公開とされるべき情報までSchemaの生成にあたり含まれてしまうことです。
自動生成によるSchemaの生成は手間がかからず、スピーディにプロジェクトを作ることができますが、意図しない情報漏洩に繋がる可能性もあります
十分に注意して使用する必要があります。

対策として、

fieldsを使用し、Schemaに含めるフィールドを指定する

  • model_fieldsSchemaに含めるフィールドを指定する
  • model_excludeSchemaから除外するフィールドを指定する
  • model_field"__all__"は基本的に使用しない(Djangoのモデル設定時に、情報漏洩の恐れのない場合のみ限定して使用する)

といった対策・設定を常に意識して、プロジェクトを進めることが重要です。

ModelSchemaでは、
model_fieldsmodel_excludeは、同時使用ができません
model_fieldsでフィールドを指定するか、model_excludeで不要なフィールドを除外するかで対応します。

ヘルパー関数(depth)

いわゆるDjangoでいうところの、ForeignKeysManyToManyのような設定です。
ModelSchemaでは、groupsという引数を設定し、この引数に渡されたSchema(予め設定されたSchemaを指定)をネストして新たなSchemaを作成します。

書き方の例は以下のとおりです。

from register.models import User
from ninja import ModelSchema, Schema
from typing import List

class ErrorSchema(Schema):
  message: str

class UserSchema(ModelSchema):
  groups: List[ErrorSchema] = []
  
  class Config:
    model = User
    model_exclude = ['password', 'last_login', 'user_permissions']

ポイントがいくつかあります。

  • ModelSchemaConfigの前にgroupsを書く
  • groupsに渡すListには、groupsを設定するSchemaより前に定義されているSchemaを渡す必要がある(順番が前後するとSchemaが定義されていないものとされてしまう)

自動生成ドキュメントを確認すると、以下のようにErrorSchemaがネストされた形となります。

groupsの設定

完全に検証できているわけではありませんが、ModelSchemaを使用し、Schemaを設定する際に設定される型については、Djangoのモデル定義の際に使用しているフィールド(CharFieldなど)により判定しているものです。
自分の意図したSchema設定になっているかを自動生成ドキュメントで確認しておくとなお安心でしょう。

ModelSchemaによりgroupsを設定すると、Schemaとしては有効と思われます。
ただし、DjangoのモデルにForeignKeyなどの設定がない場合は、基本的に空データでCRUD処理がなされると考えられます。
よって、Djangoのモデルと連動していることを考えると、データベースへの登録を予定する場合、Djangoのモデルでも同じくForeignKeyやManyToManyなどのフィールドを定義しておく必要がありそうです。
何パターンかを試しながら確認してみてください。

ここまで、前回確認したModelSchemaの内部的な実行内容とその実態についてまとめました。

Pydanticによるオーバーライド

SchemaのPydantic Config classにより、Django Ninja Schemaで利用できる様々なカスタマイズがあります。

Camel Case modeの例

1 つの興味深いConfig属性は、alias_generatorです。
Django NinjaでPydanticの例を使用すると、次のように書くことができます。

from ninja import Schema

def to_camel(string: str) -> str:
  return ''.join(word.capitalize() for word in string.split('_'))

class CamelModelSchema(Schema):
  str_field_name: str
  float_field_name: float

  class Config(Schema.Config):
    alias_generator = to_camel

渡された文字列をキャメルケースに変換する関数とSchemaを定義しています。
アンダーバーで区切った文字列を一つずつ取り出し、取り出したwordcapitalize()ですべての文字列を小文字に変換し、組み合わせています。

Django Modelを用いてConfigをカスタムする

create_schemaを使用してSchemaを生成した時、その生成されたSchemaを使用して、さらに別のSchemaを生成することができます。
書き方は以下のとおりです。

from django.contrib.auth.models import User # Djangoで定義したModel(ここではUser)をimport
from ninja.orm import create_schema # create_schemaを使用するためimport

# BaseUserSchemaとして、DjangoのUserモデルからSchemaを生成
BaseUserSchema = create_schema(User)

# 作成したBaseUserSchemaを継承し、UserSchemaを定義
class UserSchema(BaseUserSchema):
    class Config(BaseUserSchema.Config): # BaseUserSchemaのConfigをオーバーライドしていきます
        ...

上記の書き方を見ると、使い所としては、おそらく共通部分をBaseUserSchemaのように定義し、その差分を拡張していくイメージで使用することがベターかと思います。

最後に

今回は先に紹介していたDjangoのモデルからSchemaを生成する方法の、その内部で実行される仕組みについてみてみました。
ModelSchemaは、内部的にはcreate_schemaを実行していますし、Pydanticによるオーーバーライドなどの仕組みも用意されています。

プログラミングを独学中の私にとっては、使い所がまだ不明確な部分もありますが、Djangoの知識が少しある分、理解できたところもあったかなと思います。

され、次回はDjango Ninjaにおけるページネーションやレスポンスの描画な土に触れていきます。

また次回も読んでいただければと思います。
よろしくお願いします。

Django Ninjaチュートリアル12(動的なSchemaの設定)

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

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

この記事を書いた人

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

目次