Django Ninja API – ModelSchema 활용 예제

테이블을 그대로 스키마를 만들어 줄때는 그냥 ModelSchema를 사용하면 편리하다.

테이블 예제를 하나 들어 보자.



class Firmware(TimeStampedModel):
    # 고객의 소속을 위한 필드로 둠
    tenant = models.ForeignKey(Tenant, on_delete=models.PROTECT, null=False, blank=False)
    desc = models.CharField(max_length=255, default="", null=True, blank=True)
    filename = models.CharField(max_length=255)
    version = models.CharField(max_length=255)
    content = models.FileField(upload_to="uploads/", null=True)
    date_release = models.DateTimeField(default=now)
    products = models.ManyToManyField(Product, blank=True)
    is_deleted = models.IntegerField(default=0, null=True)

아래와 같이 출력해주는 Schema를 그냥 ModelSchema를 통해 자동으로 생성해 낼 수 있다.

class config: 부분을 살펴보면, 원하는 테이블을 model=테이블명 형태로 정의 하고 있다.


class FirmwareOutSchema(ModelSchema):
    # option field 혹은 None 값을 가지고 있는 필드의 경우, 아래 처럼 미리 선언해서 None이라도 대입해둬야 한다.
    desc: str = None
    # manytomany 필드의 경우 아예 해당 테이블의 out schema 로 정의해 두면, 아래 스키마에 맞게 데이터를 다 가져온다.
    products: List[ProductOutSchema] = None

    class Config:
        model = Firmware
        # model_fields = ['id', 'username', 'first_name', 'last_name']
        model_exclude = ["updated_at", "created_at"]

원하는 필드만을 뽑아 낼때는 model_fields 를 사용하고,

원하지 않는 필드만 정의하려면 model_exclude 를 사용하면 된다.

이제 API를 만들어서 JSON 형태로 답변을 주도록 만들어 보면

@router.get("/firmware", response=List[FirmwareOutSchema])
@paginate(CustomPagination)
def list_testset(request):
    # print("=== list_testset ===", request, q.dict())
    tenant_id = 1
    releases = get_user_firmware(tenantid=tenant_id).order_by("-id")
    if releases is None:
        return 404, make_message_response(404)
    return releases

페이지 기능도 넣기 위해

@paginate() 라는 wrapper 를 사용했다.

필드값들 중에 None 이나 NULL 이 들어 있으면 아래와 같은 에러가 발생하기도 한다.

  File "pydantic\main.py", line 579, in pydantic.main.BaseModel.from_orm
pydantic.error_wrappers.ValidationError: 1 validation error for NinjaResponseSchema
response -> items -> 0 -> desc
  none is not an allowed value (type=type_error.none.not_allowed)

사실 None 같은 값이 들어 있을 수도 있는데 에러를 띄우는 것을 예상하지 못했다. 그래서 귀찮아도 옵션필드들은

Out Schema 정의할때 동일한 필드명의 변수를 선언하고, 기본 값을 넣어 주도록 하자.

위의 코드를 참고하시고, 적용하면 아래와 같은 결과가 제대로 리턴된다.

{
  "total_count": 2,
  "total_pages": 1,
  "per_page": 10,
  "current_page": 1,
  "items": [
    {
      "id": 2,
      "tenant": 1,
      "desc": null,
      "filename": "openapi1.json",
      "version": "v1.0.0",
      "content": "/media/uploads/openapi_hZuZumc.json",
      "date_release": "2023-08-31T08:42:06Z",
      "is_deleted": 0,
      "products": [
        {
          "id": 1,
          "tenant": 1,
          "name": "TW100",
          "is_deleted": 0
        },
        {
          "id": 3,
          "tenant": 1,
          "name": "TW100-EVB",
          "is_deleted": 0
        }
      ]
    },
    {
      "id": 1,
...
    }
  ]
}
Response headers
![](assets/images/2023/09/image-3.png?resize=781%2C431&ssl=1)
Share: Twitter Facebook
Bongjun Hur's Picture

About Bongjun Hur

BJ is a dev.

Seoul, Korea https://devbj.com