Skip to content


Handling response is one of the most important part of any API-based application. Djapy provides a simple way to handle responses Pydatic's validators and computed fields.


The response is automatically serialized to JSON using the Pydantic model.

from djapy import Schema

class UserSchema(Schema):  # or TypedDict
    username: str
    email: str

def get_user(request, username: str) -> {200: UserSchema, 404: str}:
    user = User.objects.get(username=username)
    return user

urlpatterns = [
    path('get-user/', views.get_user, name='get-user'),
  • The response will be serialized to JSON using the UserSchema model, if not valid, pydantic error will be raised.
  • If the response is a valid instance of the model, it will be serialized to JSON and returned with a 200 status code.
  • If the response is a string, it will be returned with a 200 status code.

Invalid error response

  "error": [
      "type": "missing",
      "loc": [
      "msg": "Field required",
      "input": {
        "error": "You are not allowed to create a user"
      "url": ""
  "error_count": 1,
  "title": "output"

Computed field

Pydantic's @computed_field decorator is used to define a computed field in a schema. It is used to define a field that is not present in the model but is computed from the existing fields.

class PostSchema(Schema):
    title: str
    slug: str
    body: str

    def plain_text_body(self) -> str:
        return strip_tags(self.body)

Accessing context in computed field

You can access the context in the computed field using the context parameter.

from djapy.schema import Schema, Outsource

class LikeDataDict(TypedDict):
    like_count: int
    have_self: bool
    last_like: DetailedLikeSchema | None

class SimpleLikeSchema(Schema, Outsource):
    # ... other fields

    def likes(self) -> LikeDataDict:
        likes = PolymorphicLike.get_object_likes(self._obj).alive()
        return {
            'like_count': likes.count(),
            'have_self': likes.filter(user=self._ctx['request'].user).exists() if self._ctx[
                'request'].user.is_authenticated else False,
            'last_like': likes.last() if likes.exists() else None,

Outsource is a mixin that provides _obj and _ctx attributes to the schema. It also provides a _info as ValidationInfo object.

Accessing source object in computed field

You can access the source object in the computed field using the _obj attribute. In the above example, PolymorphicLike.get_object_likes(self._obj) is used to get the likes of the source object.

Basically, _obj is the source object that is being serialized, generally a Django model instance.


Pydantic's @field_validator or @model_validator decorator can be used to validate the fields of a schema.

from pydantic import field_validator

class PostSchema(Schema):
    title: str
    slug: str
    body: str

    @field_validator('body', mode='before')
    def assign_body(cls, body):
        linkified_body = bleach.linkify(body, callbacks=[set_nofollow, set_target])
        return bleach.clean(


How to return one Schema for multiple status codes?

You can return one schema for multiple status codes by using the uni_schema( function.

def confirm_email(request, confirmation_token: str) -> uni_schema(MessageOut, {200, 400, 422}):
    # ... your code
    return {...}  # mapped with MessageOut

You can also use uni_schema.success_2xx(YourSchema), uni_schema.error_4xx(YourSchema) or uni_schema.error_5xx(YourSchema).

How to return a list of items or QuerySet?

While retuning many-to-many relationships or a django queryset, you can use QueryList type.

from djapy.schema import QueryList

def get_users(request) -> QueryList[TodoSchema]:
    todos = Todo.objects.all()  # or filter() # or user.todos_set.all()
    return todos

How to return a image field?

You can use ImageField type to return image fields.

from djapy.schema.schema import ImageUrl

class UserSchema(Schema):
    username: str
    email: str
    profile_pic: ImageUrl