Serializing models and Querysets¶
Quote of the day
There are things that are easy.There are things that seem easy.Usually they are different things.—Nikita Sobolev, CPython core developer
Django is built around its QuerySet type.
Of course, we have to make sure that it is supported.
Prepare yourself for a wild ride! This section will not only be about queryset, but also about architecture of Django applications.
Important
We offer a brand new way of working with
QuerySet.
It is the best thing that ever happened to Django’s serialization.
We built our serialization patterns on:
Simplicity
Database table independence from the serialization schema
Customizability: you can change anything at anytime
Performance: no extra fields, no extra queries, fast serialization
Everything must be typed at all times
Oversimplied example¶
Let’s start with the very simple definition of a regular model, with no foreign keys or many-to-many fields.
Our approach is to always move all the business logic away from the view. Even in examples, because they form the habit of developers and LLMs who are reading it.
See also
Full-featured example with the proper layers can be found here:
Model¶
Our regular Model definition:
1from django.db import models
2
3
4class User(models.Model):
5 # This is sent to us from another service:
6 email = models.EmailField(unique=True)
7 customer_service_uid = models.UUIDField(unique=True)
8
9 # Our own fields:
10 is_active = models.BooleanField(db_index=True, default=True)
11 created_at = models.DateTimeField(auto_now_add=True)
12 updated_at = models.DateTimeField(auto_now=True)
For this example, the model won’t have any FK or M2M fields. In the next examples we will show how to work with them as well.
Serializer schemas¶
Next, let’s define serializer schemas to get the incoming data and return the response.
You can use any schema type, including pydantic.BaseModel,
attrs.define(), typing.TypedDict, etc.
For this example we will use pydantic, because
it is the most familiar tools for the most programmers:
1import datetime as dt
2import uuid
3from typing import Annotated, TypeAlias, final
4
5import pydantic
6
7DatabaseId: TypeAlias = Annotated[int, pydantic.Field(gt=0)]
8
9
10class SimpleUserCreateSchema(pydantic.BaseModel):
11 email: str
12 customer_service_uid: uuid.UUID
13
14
15@final
16class SimpleUserSchema(SimpleUserCreateSchema):
17 id: DatabaseId
18 created_at: dt.datetime
Important
This step is really important! Because we have to separate database models and serializer schemas from each other. Why?
Because it gives us more control over the serialization process both ways
Because new database fields will not automatically appear in your API spec
Because removing a database field will not break the API contract
Because versioning the API becomes much easier
Because explicit typing will let you catch more errors earlier
Services¶
Now, let’s define our business logic.
Note
We don’t give any architectural advice here, you can use any approach that you like for your projects: DDD, Clean or Hexagonal Architecture, Functional Core and Imperative Shell, whatever you like.
But, you business logic must be separated from views for better testing and better composition.
For this example, we will use the simplest services.py
layer for our business logic:
1from django.db import IntegrityError
2from django.db.models import QuerySet
3
4from server.apps.model_simple.models import User
5from server.apps.model_simple.serializers import SimpleUserCreateSchema
6
7
8class UniqueConstraintError(Exception):
9 """Fields ``email`` and ``customer_service_uid`` must be unique."""
10
11
12def user_create_service(user_schema: SimpleUserCreateSchema) -> User:
13 """This is a function just for the demo purpose, it is usually a class."""
14 try:
15 return User.objects.create(
16 email=user_schema.email,
17 customer_service_uid=user_schema.customer_service_uid,
18 )
19 except IntegrityError:
20 # We don't raise `IntegrityError` here, because we prefer domain
21 # exceptions over Django ones. It is much easier to manage.
22 raise UniqueConstraintError from None
23
24
25def user_list_service() -> QuerySet[User]:
26 """Return all users."""
27 # Only can exclude fields that we won't use by passing as a param
28 # `UserSchema.__struct_fields__` tuple and calling `.only(*fields)` here:
29 return User.objects.all()
There’s nothing fancy about it. Just creating a model from the typed input data.
Views¶
Now, we can define views that will use everything from the above.
Just convert models from attributes,
using the builtin .model_validate() converter.
For msgspec one can use msgspec.convert().
The main reason to use this approach is that it is short and easy
The main reason not to use this approach is that errors will only show up during tests. Not during type-checking. For example, removing
customer_service_uidfrommodels.py(for some business reason) will not trigger any type checking errors. If this line is not covered with tests, your API will not work:Traceback (most recent call last): File "server/apps/model_simple/views/minimalistic.py", line 42 return UserSchema.model_validate( ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ pydantic.ValidationError: Object missing required field `customer_service_uid`
1from http import HTTPStatus
2from typing import Final, final
3
4import pydantic
5from django.http import HttpResponse
6from typing_extensions import override
7
8from dmr import Body, Controller, modify
9from dmr.endpoint import Endpoint
10from dmr.errors import ErrorType
11from dmr.metadata import ResponseSpec
12from dmr.plugins.pydantic import PydanticSerializer
13from server.apps.model_simple.serializers import (
14 SimpleUserCreateSchema,
15 SimpleUserSchema,
16)
17from server.apps.model_simple.services import (
18 UniqueConstraintError,
19 user_create_service,
20 user_list_service,
21)
22
23_UserList: Final = pydantic.TypeAdapter(list[SimpleUserSchema])
24
25
26@final
27class UserController(Controller[PydanticSerializer]):
28 def get(self) -> list[SimpleUserSchema]:
29 """List existing users."""
30 return _UserList.validate_python(
31 user_list_service(),
32 from_attributes=True,
33 )
34
35 @modify(
36 extra_responses=[
37 ResponseSpec(
38 Controller.error_model,
39 status_code=HTTPStatus.CONFLICT,
40 ),
41 ],
42 )
43 def post(
44 self,
45 parsed_body: Body[SimpleUserCreateSchema],
46 ) -> SimpleUserSchema:
47 """Create new user."""
48 return SimpleUserSchema.model_validate(
49 user_create_service(parsed_body),
50 from_attributes=True,
51 )
52
53 @override
54 def handle_error(
55 self,
56 endpoint: Endpoint,
57 controller: Controller[PydanticSerializer],
58 exc: Exception,
59 ) -> HttpResponse:
60 # Handle custom errors that can happen in this controller:
61 if isinstance(exc, UniqueConstraintError):
62 return self.to_error(
63 self.format_error(
64 'User `email` and `customer_service_uid` must be unique',
65 error_type=ErrorType.value_error,
66 ),
67 status_code=HTTPStatus.CONFLICT,
68 )
69 # Handle default errors:
70 return super().handle_error(endpoint, controller, exc)
71
Run result
$ curl http://127.0.0.1:8000/api/users/ -X POST -d '{"email": "minimalistic@example.com", "customer_service_uid": "e87035e1-27a6-4e6b-a61a-d395bd4e221a"}' -H 'Content-Type: application/json'
{"email":"minimalistic@example.com","customer_service_uid":"e87035e1-27a6-4e6b-a61a-d395bd4e221a","id":1,"created_at":"2026-05-07T12:58:18.904281Z"}
$ curl http://127.0.0.1:8000/api/users/ -X GET
[{"email":"minimalistic@example.com","customer_service_uid":"e87035e1-27a6-4e6b-a61a-d395bd4e221a","id":1,"created_at":"2026-05-07T12:58:18.904281Z"}]
Convert models to schemas manually. It might seem like a lot of code, but actually, it is pretty simple to do with (especially with the help of LLM).
The main reason to use this approach is correctness. For example, removing
customer_service_uidmodel field will now trigger an early type-checking error, unlike the “Minimalistic” version, which will only fail in runtime:server/apps/model_simple/views/detailed.py:28: error: "User" has no attribute "customer_service_uid" [attr-defined] server/apps/model_simple/views/detailed.py:48: error: "User" has no attribute "customer_service_uid" [attr-defined]
The main reason not to use this approach is its verbosity
1from http import HTTPStatus
2from typing import final
3
4from django.http import HttpResponse
5from typing_extensions import override
6
7from dmr import Body, Controller, modify
8from dmr.endpoint import Endpoint
9from dmr.errors import ErrorType
10from dmr.metadata import ResponseSpec
11from dmr.plugins.pydantic import PydanticSerializer
12from server.apps.model_simple.serializers import (
13 SimpleUserCreateSchema,
14 SimpleUserSchema,
15)
16from server.apps.model_simple.services import (
17 UniqueConstraintError,
18 user_create_service,
19 user_list_service,
20)
21
22
23@final
24class UserController(Controller[PydanticSerializer]):
25 def get(self) -> list[SimpleUserSchema]:
26 """List existing users."""
27 return [
28 SimpleUserSchema(
29 id=user.pk,
30 email=user.email,
31 customer_service_uid=user.customer_service_uid,
32 created_at=user.created_at,
33 )
34 for user in user_list_service()
35 ]
36
37 @modify(
38 extra_responses=[
39 ResponseSpec(
40 Controller.error_model,
41 status_code=HTTPStatus.CONFLICT,
42 ),
43 ],
44 )
45 def post(
46 self,
47 parsed_body: Body[SimpleUserCreateSchema],
48 ) -> SimpleUserSchema:
49 """Create new user."""
50 user = user_create_service(parsed_body)
51 return SimpleUserSchema(
52 id=user.pk,
53 email=user.email,
54 customer_service_uid=user.customer_service_uid,
55 created_at=user.created_at,
56 )
57
58 @override
59 def handle_error(
60 self,
61 endpoint: Endpoint,
62 controller: Controller[PydanticSerializer],
63 exc: Exception,
64 ) -> HttpResponse:
65 # Handle custom errors that can happen in this controller:
66 if isinstance(exc, UniqueConstraintError):
67 return self.to_error(
68 self.format_error(
69 'User `email` and `customer_service_uid` must be unique',
70 error_type=ErrorType.value_error,
71 ),
72 status_code=HTTPStatus.CONFLICT,
73 )
74 # Handle default errors:
75 return super().handle_error(endpoint, controller, exc)
76
Run result
$ curl http://127.0.0.1:8000/api/users/ -X POST -d '{"email": "detailed@example.com", "customer_service_uid": "616d51f2-2c31-4b04-8034-2f59eae042db"}' -H 'Content-Type: application/json'
{"email":"detailed@example.com","customer_service_uid":"616d51f2-2c31-4b04-8034-2f59eae042db","id":2,"created_at":"2026-05-07T12:58:20.066877Z"}
$ curl http://127.0.0.1:8000/api/users/ -X GET
[{"email":"minimalistic@example.com","customer_service_uid":"e87035e1-27a6-4e6b-a61a-d395bd4e221a","id":1,"created_at":"2026-05-07T12:58:18.904281Z"},{"email":"detailed@example.com","customer_service_uid":"616d51f2-2c31-4b04-8034-2f59eae042db","id":2,"created_at":"2026-05-07T12:58:20.066877Z"}]
Now you have your REST API that returns fully typed model responses
and can work with QuerySet
and Model instances.
Realistic example¶
Now, let’s see how a more realistic layout may look like. It will include:
ForeignKeyrelationshipManyToManyrelationshipDI for realistic expectations
Mappers and services separate layers
Models¶
We start with models definitions.
1from django.db import models
2
3
4class Tag(models.Model):
5 name = models.CharField(max_length=100, unique=True)
6
7 created_at = models.DateTimeField(auto_now_add=True)
8 updated_at = models.DateTimeField(auto_now=True)
9
10
11class Role(models.Model):
12 name = models.CharField(max_length=100, unique=True)
13
14 created_at = models.DateTimeField(auto_now_add=True)
15 updated_at = models.DateTimeField(auto_now=True)
16
17
18class User(models.Model):
19 # This is sent to us from another service:
20 email = models.EmailField(unique=True)
21
22 # Linking:
23 role = models.ForeignKey(
24 Role,
25 on_delete=models.CASCADE,
26 related_name='users',
27 )
28 tags = models.ManyToManyField(Tag, related_name='users')
29
30 # Our own fields:
31 is_active = models.BooleanField(db_index=True, default=True)
32 created_at = models.DateTimeField(auto_now_add=True)
33 updated_at = models.DateTimeField(auto_now=True)
We added two models:
Rolefor foreign key relationTagfor many-to-many relation
Serializer schemas¶
Next, let’s see how serializer schemas are defined.
1import datetime as dt
2from typing import Annotated, TypeAlias, final
3
4import pydantic
5
6DatabaseId: TypeAlias = Annotated[int, pydantic.Field(gt=0)]
7
8
9@final
10class PageQuery(pydantic.BaseModel):
11 page_size: int = pydantic.Field(default=10, ge=1, le=100)
12 page: int = pydantic.Field(default=1, ge=1)
13
14 model_config = pydantic.ConfigDict(extra='forbid')
15
16
17@final
18class TagSchema(pydantic.BaseModel):
19 name: str = pydantic.Field(max_length=100)
20
21
22@final
23class RoleSchema(pydantic.BaseModel):
24 name: str = pydantic.Field(max_length=100)
25
26
27class UserCreateSchema(pydantic.BaseModel):
28 email: str
29 role: RoleSchema
30 tags: list[TagSchema]
31
32
33@final
34class UserSchema(UserCreateSchema):
35 id: DatabaseId
36 created_at: dt.datetime
Note that we model foreign key and many-to-many relations
here as nested schemas or list of nested schemas.
However, you can also model the same thing
as role_id: int and tags: list[int] to support ids for linking.
That’s the beautify of this extremely simple approach:
it is customizable to the core.
Views¶
In this example, it would be easier to start with views.py:
1from http import HTTPStatus
2from typing import final
3
4from django.http import HttpResponse
5from typing_extensions import override
6
7from dmr import Body, Controller, Query, modify
8from dmr.endpoint import Endpoint
9from dmr.errors import ErrorType
10from dmr.metadata import ResponseSpec
11from dmr.pagination import Paginated
12from dmr.plugins.pydantic import PydanticSerializer
13from server.apps.model_fk.implemented import HasContainer
14from server.apps.model_fk.serializers import (
15 PageQuery,
16 UserCreateSchema,
17 UserSchema,
18)
19from server.apps.model_fk.services import (
20 UniqueConstraintError,
21 UserCreate,
22 UserList,
23)
24
25
26@final
27class UserController(HasContainer, Controller[PydanticSerializer]):
28 def get(self, parsed_query: Query[PageQuery]) -> Paginated[UserSchema]:
29 """List existing users."""
30 return self.resolve(UserList)(parsed_query)
31
32 @modify(
33 extra_responses=[
34 ResponseSpec(
35 Controller.error_model,
36 status_code=HTTPStatus.CONFLICT,
37 ),
38 ],
39 )
40 def post(self, parsed_body: Body[UserCreateSchema]) -> UserSchema:
41 """Create new user."""
42 return self.resolve(UserCreate)(parsed_body)
43
44 @override
45 def handle_error(
46 self,
47 endpoint: Endpoint,
48 controller: Controller[PydanticSerializer],
49 exc: Exception,
50 ) -> HttpResponse:
51 # Handle custom errors that can happen in this controller:
52 if isinstance(exc, UniqueConstraintError):
53 return self.to_error(
54 self.format_error(
55 'User `email` must be unique',
56 error_type=ErrorType.value_error,
57 ),
58 status_code=HTTPStatus.CONFLICT,
59 )
60 # Handle default errors:
61 return super().handle_error(endpoint, controller, exc)
62
Run result
$ curl http://127.0.0.1:8000/api/users/ -X POST -d '{"email": "test@example.com", "role": {"name": "admin"}, "tags": [{"name": "paid"}]}' -H 'Content-Type: application/json'
{"email":"test@example.com","role":{"name":"admin"},"tags":[{"name":"paid"}],"id":1,"created_at":"2026-05-07T12:58:21.226056Z"}
$ curl http://127.0.0.1:8000/api/users/ -X GET
{"count":1,"num_pages":1,"per_page":10,"page":{"number":1,"object_list":[{"email":"test@example.com","role":{"name":"admin"},"tags":[{"name":"paid"}],"id":1,"created_at":"2026-05-07T12:58:21.226056Z"}]}}
What happens here?
We refactored our views to only run an instance of a specific service, which we get using
HasContainer.resolvecall, which is our DIWe now don’t construct any serializer schemas inside our views, we move to its independent infra layer
We now use Pagination to list all users
Our views here are reduced to a single line of code, which do everything inside the business logic. Exactly the way it should be for scalable and reliable applications.
DI¶
In this example we use punq as a simplistic DI container to show how big projects really handle such cases.
Note
You are not forced to use punq, we don’t enforce any DI framework.
Our principle is to Bring your own DI.
The DI part looks like this:
1from typing import Any, TypeVar
2
3# TODO(sobolevn): release new `punq` version:
4import punq # type: ignore[import-untyped]
5
6from server.apps.model_fk.mappers import RoleMap, TagMap, UserMap
7from server.apps.model_fk.services import (
8 RoleCreate,
9 TagsCreate,
10 UserCreate,
11 UserList,
12)
13
14_ItemT = TypeVar('_ItemT')
15
16
17class HasContainer:
18 __slots__ = ('_container',)
19
20 def __init__(self, *args: Any, **kwargs: Any) -> None:
21 super().__init__(*args, **kwargs)
22 # Will be created in import-time:
23 self._container = self._create_container()
24
25 def resolve(self, thing: type[_ItemT]) -> _ItemT:
26 return self._container.resolve(thing) # type: ignore[no-any-return]
27
28 def _create_container(self) -> punq.Container:
29 container = punq.Container()
30 container.register(TagMap)
31 container.register(RoleMap)
32 container.register(UserMap)
33
34 container.register(TagsCreate)
35 container.register(RoleCreate)
36 container.register(UserCreate)
37 container.register(UserList)
38
39 return container
What happens here?
We define a class that will be used as a mixin for all future controllers
This class provides a pre-built container with all the dependencies
Users can call
self.resolveinside controllers to resolve specific dependencies
Now, let’s see how we create objects in the database.
Services¶
Again, this is just an example. You are not forced to create this specific service-based architecture. Use whatever layers separation practice as you want. Our big example uses usecases as the main logic entities and entry points.
However, our services.py is the simplest and the shortest way.
That’s why we are using them as an example:
1from collections.abc import Iterable
2from typing import final
3
4import attrs
5from django.db import IntegrityError, transaction
6
7from dmr.pagination import Paginated
8from server.apps.model_fk.mappers import UserMap
9from server.apps.model_fk.models import Role, Tag, User
10from server.apps.model_fk.serializers import (
11 PageQuery,
12 RoleSchema,
13 TagSchema,
14 UserCreateSchema,
15 UserSchema,
16)
17
18
19@final
20class UniqueConstraintError(Exception):
21 """Field ``email`` must be unique."""
22
23
24@final
25@attrs.define(frozen=True)
26class TagsCreate:
27 def __call__(self, tags: list[TagSchema]) -> Iterable[Tag]:
28 """Service for creating new tags with unique names."""
29 tags_names = {tag.name for tag in tags}
30 # Create new tags and ignore existing ones in 1 query:
31 Tag.objects.bulk_create(
32 [Tag(name=tag) for tag in tags_names],
33 ignore_conflicts=True,
34 )
35 # Since `bulk_create` does not return `ids`, we need to query them:
36 return Tag.objects.in_bulk(
37 tags_names,
38 field_name='name',
39 ).values()
40
41
42@final
43@attrs.define(frozen=True)
44class RoleCreate:
45 def __call__(self, role_schema: RoleSchema) -> Role:
46 """Service for getting an existing role or creating a new one."""
47 role, _ = Role.objects.get_or_create(name=role_schema.name)
48 return role
49
50
51@final
52@attrs.define(frozen=True)
53class UserCreate:
54 _role_create: RoleCreate
55 _tags_create: TagsCreate
56 _mapper: UserMap
57
58 def __call__(self, user_schema: UserCreateSchema) -> UserSchema:
59 """Service to create new users."""
60 return self._mapper.single(self._create_user(user_schema))
61
62 def _create_user(self, user_schema: UserCreateSchema) -> User:
63 with transaction.atomic():
64 role = self._role_create(user_schema.role)
65
66 try:
67 user = User.objects.create(
68 email=user_schema.email,
69 role=role,
70 )
71 except IntegrityError:
72 # We don't raise `IntegrityError` here, because we prefer domain
73 # exceptions over Django ones. It is much easier to manage.
74 raise UniqueConstraintError from None
75
76 # Handle m2m:
77 user.tags.set(self._tags_create(user_schema.tags))
78 return user
79
80
81@final
82@attrs.define(frozen=True)
83class UserList:
84 _mapper: UserMap
85
86 def __call__(self, parsed_query: PageQuery) -> Paginated[UserSchema]:
87 """Return all users."""
88 return self._mapper.multiple(
89 (
90 User.objects
91 .select_related('role')
92 .prefetch_related('tags')
93 .order_by('id')
94 .all() # it is still lazy
95 ),
96 parsed_query,
97 )
What happens here?
We define three services: one per create operation
Some of them have dependencies defined as dataclass fields, like
_mapper: UserMap, these fields will be resolved by our DIEach service does just a single thing, it would be easy to compose them
Now we are ready for the final layer: mapping of the created database models.
Mappers¶
Mappers just map database models into serialization schemas.
Tip
In real projects, it is better to have a separate layer that does mapping of database models into serialization schemas
It will allow you to compose mappers freely. For example, it allows you to create compatibility layers, when some model have some database fields removed, but still need a way to send users the same API schema.
Or it can do some small representation logic, like combining
first_name and last_name of users into full_name.
Or handle Pagination, as we do in this example.
1from typing import final
2
3import attrs
4from django.core.paginator import EmptyPage, Paginator
5from django.db.models import QuerySet
6
7from dmr.pagination import Page, Paginated
8from server.apps.model_fk.models import Role, Tag, User
9from server.apps.model_fk.serializers import (
10 PageQuery,
11 RoleSchema,
12 TagSchema,
13 UserSchema,
14)
15
16
17@final
18@attrs.define(frozen=True)
19class TagMap:
20 def single(self, tag: Tag) -> TagSchema:
21 return TagSchema(name=tag.name)
22
23 def multiple(self, tags: QuerySet[Tag]) -> list[TagSchema]:
24 return [self.single(tag) for tag in tags]
25
26
27@final
28@attrs.define(frozen=True)
29class RoleMap:
30 def single(self, role: Role) -> RoleSchema:
31 return RoleSchema(name=role.name)
32
33
34@final
35@attrs.define(frozen=True)
36class UserMap:
37 _role: RoleMap
38 _tag: TagMap
39
40 def single(self, user: User) -> UserSchema:
41 return UserSchema(
42 id=user.pk,
43 email=user.email,
44 created_at=user.created_at,
45 role=self._role.single(user.role),
46 tags=self._tag.multiple(user.tags.all()),
47 )
48
49 def multiple(
50 self,
51 users: QuerySet[User],
52 parsed_query: PageQuery,
53 ) -> Paginated[UserSchema]:
54 # Logic to paginate users:
55 paginator = Paginator(users, parsed_query.page_size)
56 try:
57 object_list = [
58 self.single(user)
59 for user in paginator.page(parsed_query.page).object_list
60 ]
61 except EmptyPage:
62 object_list = []
63 return Paginated(
64 count=paginator.count,
65 num_pages=paginator.num_pages,
66 per_page=paginator.per_page,
67 page=Page(
68 number=parsed_query.page,
69 object_list=object_list,
70 ),
71 )
Now we have a fully working application. With composable and flexible schemas, which will:
Report any type errors early
Be customizable to the core
Can be used in a good architecture in big real business apps
Change independently from models
Conclusions¶
Here’s the final table to help you decide what to use:
Your App |
What to Use |
|---|---|
Todo App |
Minimalistic Example |
Real Application |
Mappers |
django-mantle¶
If you want to automate the mapping part and automagically
convert QuerySet into typed models, you can use
django-mantle.
Allows you to move your business logic into type-safe Python classes, decoupled from the Django ORM.
Provides automatic generation (with declarative overrides) of efficient ORM queries, including limited field fetches with
only()anddefer()and prefetching related objects, avoiding N+1 query problems.Uses a modern and performant approach to serialisation and validation.
Provides a progressive API, with a minimal surface area by default, and depth when needed.
It’s your type-safe layer around Django’s liquid core.
1import attrs
2
3from dmr import Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7@attrs.define
8class _UserModel:
9 username: str
10 email: str
11 is_active: bool
12
13
14class UsersController(Controller[MsgspecSerializer]):
15 def get(self) -> list[_UserModel]:
16 # We have to do import here due to how our docs build systems works,
17 # but in real apps they must be on the module level:
18 from django.contrib.auth.models import User # noqa: PLC0415
19 from mantle import Query # noqa: PLC0415
20
21 return Query(User.objects.all(), _UserModel).all()
22
Run result
$ curl http://127.0.0.1:8000/api/users/ -X GET
[{"email":"test@example.com","is_active":true,"username":"test_user"}]
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": [
"msg"
],
"title": "ErrorDetail",
"type": "object"
},
"ErrorModel": {
"description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ErrorDetail"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_UserModel": {
"properties": {
"email": {
"type": "string"
},
"is_active": {
"type": "boolean"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"email",
"is_active"
],
"title": "_UserModel",
"type": "object"
}
},
"securitySchemes": {}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/userscontroller/": {
"get": {
"deprecated": false,
"operationId": "getUserscontrollerApiUserscontroller",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/_UserModel"
},
"type": "array"
}
}
},
"description": "OK"
},
"406": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when provided `Accept` header cannot be satisfied"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when returned response does not match the response schema"
}
}
}
}
}
}