Repository: vitalik/django-ninja

Issue:
Foreign key field value instead of id without nesting schemas ?
Hi, is it possible to send a foreign key value without nesting the schemas ? Here's my scenario: 

```python
class Department(models.Model):
    id = models.IntegerField(primarty_key=True, editable=False)
    dep = models.CharField(max_length=200, null=False,unique=True)

 class Student(models.Model):
    id = models.IntegerField(primarty_key=True, editable=False)
    name = models.CharField(max_length=200, null=False)
    dep = models.ForeignKey(Department, on_delete=models.CASCADE)


 class DepartmentOut(Schema):
     dep: str
 

 class StudentOut(Schema):
    id: int
    name: str
    dep: DepartmentOut

```
this will result in an output of: 
```python
{
    id: 1000,
    name: 'Some name',
    dep: {
        dep: 'Some Text'
        }
        
}
```

How can I show the dep string without a nested object called dep? Like the below shape of JSON:

```python
{
    id: 1000,
    name: 'Some name',
    dep: 'Some Text'
    
}
```
I tried searching without any luck, thanks in advance.

Discussion:
SmileyChris: Fixes #250 too
SmileyChris: I bumped the pydantic version to 1.9 to support the fancier `DjangoGetter` method being used. The alternative is to backport the pydantic fix to a method on the ninja `Schema` class: 
```
    @classmethod
    def _decompose_class(cls, obj: Any) -> GetterDict:
        if isinstance(obj, GetterDict):
            return obj
        return cls._decompose_class(obj)
```
vitalik: > I bumped the pydantic version to 1.9
well this cannot be merged - there ALOT projects that depends on it 
I would keep minimum 1.6 pydantic while it still not breakable

Patch:
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 643879f..9cb32bd 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -23,14 +23,3 @@ jobs:
           FLIT_USERNAME: ${{ secrets.FLIT_USERNAME }}
           FLIT_PASSWORD: ${{ secrets.FLIT_PASSWORD }}
         run: flit publish
-
-  docs:
-    runs-on: ubuntu-latest
-    
-    steps:
-      - uses: actions/checkout@v2
-        with:
-          fetch-depth: 0
-      - name: Docs update
-        # run: git push origin master:docs
-        run: git branch -a
diff --git a/LICENSE b/LICENSE
index 50ade93..8105f68 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2022 Vitaliy Kucheryaviy
+Copyright (c) 2020 Vitaliy Kucheryaviy
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index d2a2d1e..467c6bc 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
  **Key features:**
 
   - **Easy**: Designed to be easy to use and intuitive.
-  - **FAST execution**: Very high performance thanks to **<a href="https://pydantic-docs.helpmanual.io" target="_blank">Pydantic</a>** and **<a href="/docs/docs/async-support.md">async support</a>**.
+  - **FAST execution**: Very high performance thanks to **<a href="https://pydantic-docs.helpmanual.io" target="_blank">Pydantic</a>** and **<a href="/async-support/">async support</a>**.
   - **Fast to code**: Type hints and automatic docs lets you focus only on business logic.
   - **Standards-based**: Based on the open standards for APIs: **OpenAPI** (previously known as Swagger) and **JSON Schema**.
   - **Django friendly**: (obviously) has good integration with the Django core and ORM.
diff --git a/docs/docs/tutorial/response-schema.md b/docs/docs/tutorial/response-schema.md
index fdfbc23..fecd511 100644
--- a/docs/docs/tutorial/response-schema.md
+++ b/docs/docs/tutorial/response-schema.md
@@ -90,7 +90,7 @@ class TaskSchema(Schema):
 
 @api.get("/tasks", response=List[TaskSchema])
 def tasks(request):
-    queryset = Task.objects.all()
+    queryset = Task.objects.select_related("owner")
     return list(queryset)
 ```
 
@@ -117,6 +117,59 @@ If you execute this operation, you should get a response like this:
 ]
 ```
 
+
+## Aliases
+
+Instead of a nested response, you may want to just flatten the response output.
+The Ninja `Schema` object extends Pydantic's `Field(..., alias="")` format to
+work with dotted responses.
+
+Using the models from above, let's make a schema that just includes the task
+owner's first name inline, and also uses `completed` rather than `is_completed`:
+
+```Python hl_lines="1 7-9"
+from ninja import Field, Schema
+
+
+class TaskSchema(Schema):
+    id: int
+    title: str
+    # The first Field param is the default, use ... for required fields.
+    completed: bool = Field(..., alias="is_completed)
+    owner_first_name: str = Field(None, alias="owner.first_name")
+```
+
+
+## Resolvers
+
+You can also create calculated fields via resolve methods based on the field
+name.
+
+The method must accept a single argument, which will be the object the schema
+is resolving against.
+
+When creating a resolver as a standard method, `self` gives you access to other
+validated and formatted attributes in the schema.
+
+```Python hl_lines="5 7-11"
+class TaskSchema(Schema):
+    id: int
+    title: str
+    is_completed: bool
+    owner: Optional[str]
+    lower_title: str
+
+    @staticmethod
+    def resolve_owner(obj):
+        if not obj.owner:
+            return
+        return f"{obj.owner.first_name} {obj.owner.last_name}"
+
+    def resolve_lower_title(self, obj):
+        return self.title.lower()
+```
+
+
 ## Returning querysets
 
 In the previous example we specifically converted a queryset into a list (and executed the SQL query during evaluation).
diff --git a/docs/docs/tutorial/urls.md b/docs/docs/tutorial/urls.md
deleted file mode 100644
index 24084bf..0000000
--- a/docs/docs/tutorial/urls.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Reverse Resolution of URLS
-
-A reverse URL name is generated for each method in a Django Ninja Schema (or `Router`).
-
-## How URLs are generated
-
-The URLs are all contained within a namespace, which defaults to `"api-1.0.0"`, and each URL name matches the function it is decorated. 
-
-For example:
-
-```Python
-api = NinjaAPI()
-
-@api.get("/")
-def index(request):
-    ...
-
-index_url = reverse_lazy("api-1.0.0:index")
-```
-
-### Changing the URL name
-
-Rather than using the default URL name, you can specficy it explicitly as a property on the method decorator.
-
-```Python
-@api.get("/users", url_name="user_list")
-def users(request):
-    ...
-
-users_url = reverse_lazy("api-1.0.0:user_list")
-```
-### Customizing the namespace
-
-The default URL namespace is built by prepending the Schema's version with `"api-"`, however you can explicitly specify the namespace by overriding the `urls_namespace` attribute of the `NinjaAPI` Schema class.
-
-```Python
-
-api = NinjaAPI(auth=token_auth, version='2')
-api_private = NinjaAPI(auth=session_auth, urls_namespace='private_api')
-
-api_users_url = reverse_lazy("api-2:users")
-private_api_admins_url = reverse_lazy("private_api:admins")
-```
\ No newline at end of file
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index fd9dac7..7cbeb2c 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -3,10 +3,10 @@ site_description: Django Ninja - Django REST framework with high performance, ea
 site_url: https://django-ninja.rest-framework.com
 repo_name: vitalik/django-ninja
 repo_url: https://github.com/vitalik/django-ninja
-edit_uri: ""
+edit_uri: ''
 google_analytics:
-  - UA-170474207-1
-  - auto
+- UA-170474207-1
+- auto
 theme:
   name: material
   palette:
@@ -17,45 +17,44 @@ theme:
   icon:
     repo: fontawesome/brands/github-alt
 nav:
-  - Intro: index.md
-  - motivation.md
-  - Tutorial / Reference:
-      - tutorial/index.md
-      - tutorial/path-params.md
-      - tutorial/query-params.md
-      - tutorial/body.md
-      - tutorial/form-params.md
-      - tutorial/file-params.md
-      - tutorial/response-schema.md
-      - tutorial/crud.md
-      - tutorial/django-pydantic.md
-      - Advanced Schema:
-          - tutorial/django-pydantic-create-schema.md
-          - tutorial/config-pydantic.md
-      - tutorial/authentication.md
-      - tutorial/routers.md
-      - tutorial/urls.md
-      - tutorial/operation_params.md
-      - tutorial/versioning.md
-      - tutorial/request-parsers.md
-      - tutorial/response-renderers.md
-      - tutorial/errors.md
-      - tutorial/csrf.md
-  - async-support.md
-  - video.md
-  - Enhancement Proposals:
-      - Intro: proposals/index.md
-      - proposals/cbv.md
-      - proposals/v1.md
-  - help.md
-  - releases.md
+- Intro: index.md
+- motivation.md
+- Tutorial / Reference:
+  - tutorial/index.md
+  - tutorial/path-params.md
+  - tutorial/query-params.md
+  - tutorial/body.md
+  - tutorial/form-params.md
+  - tutorial/file-params.md
+  - tutorial/response-schema.md
+  - tutorial/crud.md
+  - tutorial/django-pydantic.md
+  - Advanced Schema:
+    - tutorial/django-pydantic-create-schema.md
+    - tutorial/config-pydantic.md
+  - tutorial/authentication.md
+  - tutorial/routers.md
+  - tutorial/operation_params.md
+  - tutorial/versioning.md
+  - tutorial/request-parsers.md
+  - tutorial/response-renderers.md
+  - tutorial/errors.md
+  - tutorial/csrf.md
+- async-support.md
+- video.md
+- Enhancement Proposals:
+  - Intro: proposals/index.md
+  - proposals/cbv.md
+  - proposals/v1.md
+- help.md
+- releases.md
 markdown_extensions:
-  - markdown_include.include
-  - markdown.extensions.codehilite:
-      guess_lang: false
-  # Uncomment these 2 lines during development to more easily add highlights
-  #- pymdownx.highlight:
-  #    linenums: true
-  - codehilite
-  - admonition
-  - pymdownx.superfences
+- markdown_include.include
+- markdown.extensions.codehilite:
+    guess_lang: false
+# Uncomment these 2 lines during development to more easily add highlights
+#- pymdownx.highlight:
+#    linenums: true
+- codehilite
+- admonition
+- pymdownx.superfences
diff --git a/ninja/__init__.py b/ninja/__init__.py
index 7a32e8a..edd9380 100644
--- a/ninja/__init__.py
+++ b/ninja/__init__.py
@@ -1,6 +1,6 @@
 """Django Ninja - Fast Django REST framework"""
 
-__version__ = "0.16.2"
+__version__ = "0.16.1"
 
 from pydantic import Field
 
diff --git a/ninja/openapi/schema.py b/ninja/openapi/schema.py
index 0c59c0a..9fc797b 100644
--- a/ninja/openapi/schema.py
+++ b/ninja/openapi/schema.py
@@ -151,11 +151,6 @@ class OpenAPISchema(dict):
                     "schema": p_schema,
                     "required": p_required,
                 }
-
-                # copy description from schema description to param description
-                if "description" in p_schema:
-                    param["description"] = p_schema["description"]
-
                 result.append(param)
 
         return result
diff --git a/ninja/orm/metaclass.py b/ninja/orm/metaclass.py
index 652b34c..1300238 100644
--- a/ninja/orm/metaclass.py
+++ b/ninja/orm/metaclass.py
@@ -1,16 +1,15 @@
 from typing import no_type_check
 
 from django.db.models import Model as DjangoModel
-from pydantic.main import ModelMetaclass
 
 from ninja.errors import ConfigError
 from ninja.orm.factory import create_schema
-from ninja.schema import Schema
+from ninja.schema import ResolverMetaclass, Schema
 
 _is_modelschema_class_defined = False
 
 
-class ModelSchemaMetaclass(ModelMetaclass):
+class ModelSchemaMetaclass(ResolverMetaclass):
     @no_type_check
     def __new__(
         mcs,
diff --git a/ninja/schema.py b/ninja/schema.py
index 73b762f..b86a722 100644
--- a/ninja/schema.py
+++ b/ninja/schema.py
@@ -1,9 +1,33 @@
-from typing import Any
+"""
+Since "Model" word would be very confusing when used in django context, this
+module basically makes an alias for it named "Schema" and adds extra whistles to
+be able to work with django querysets and managers.
+
+The schema is a bit smarter than a standard pydantic Model because it can handle
+dotted attributes and resolver methods. For example::
+
+
+    class UserSchema(User):
+        name: str
+        initials: str
+        boss: str = Field(None, alias="boss.first_name")
+
+        @staticmethod
+        def resolve_name(obj):
+            return f"{obj.first_name} {obj.last_name}"
+
+        def resolve_initials(self, obj):
+            return "".join(n[:1] for n in self.name.split())
+
+"""
+from operator import attrgetter
+from typing import Any, Callable, Dict, Type, TypeVar, Union, no_type_check
 
 import pydantic
 from django.db.models import Manager, QuerySet
 from django.db.models.fields.files import FieldFile
 from pydantic import BaseModel, Field, validator
+from pydantic.main import ModelMetaclass
 from pydantic.utils import GetterDict
 
 pydantic_version = list(map(int, pydantic.VERSION.split(".")[:2]))
@@ -11,16 +35,37 @@ assert pydantic_version >= [1, 6], "Pydantic 1.6+ required"
 
 __all__ = ["BaseModel", "Field", "validator", "DjangoGetter", "Schema"]
 
-
-# Since "Model" word would be very confusing when used in django context
-# this module basically makes alias for it named "Schema"
-# and ads extra whistles to be able to work with django querysets and managers
+S = TypeVar("S", bound="Schema")
 
 
 class DjangoGetter(GetterDict):
+    __slots__ = ("_obj", "_schema_cls")
+
+    def __init__(self, obj: Any, schema_cls: "Type[Schema]"):
+        self._obj = obj
+        self._schema_cls = schema_cls
+
+    def __getitem__(self, key: str) -> Any:
+        resolver = self._schema_cls._ninja_resolvers.get(key)
+        if resolver:
+            item = resolver(getter=self)
+        else:
+            try:
+                item = getattr(self._obj, key)
+            except AttributeError:
+                try:
+                    item = attrgetter(key)(self._obj)
+                except AttributeError as e:
+                    raise KeyError(key) from e
+        return self.format_result(item)
+
     def get(self, key: Any, default: Any = None) -> Any:
-        result = super().get(key, default)
+        try:
+            return self[key]
+        except KeyError:
+            return default
 
+    def format_result(self, result: Any) -> Any:
         if isinstance(result, Manager):
             return list(result.all())
 
@@ -35,7 +80,87 @@ class DjangoGetter(GetterDict):
         return result
 
 
-class Schema(BaseModel):
+class Resolver:
+    __slots__ = ("_func", "_static")
+    _static: bool
+    _func: Any
+
+    def __init__(self, func: Union[Callable, staticmethod]):
+        if isinstance(func, staticmethod):
+            self._static = True
+            self._func = func.__func__
+        else:
+            self._static = False
+            self._func = func
+
+    def __call__(self, getter: DjangoGetter) -> Any:
+        if self._static:
+            return self._func(getter._obj)
+        return self._func(self._fake_instance(getter), getter._obj)
+
+    def _fake_instance(self, getter: DjangoGetter) -> "Schema":
+        """
+        Generate a partial schema instance that can be used as the ``self``
+        attribute of resolver functions.
+        """
+
+        class PartialSchema(Schema):
+            def __getattr__(self, key: str) -> Any:
+                value = getter[key]
+                field = getter._schema_cls.__fields__[key]
+                value = field.validate(value, values={}, loc=key, cls=None)[0]
+                return value
+
+        return PartialSchema()
+
+
+class ResolverMetaclass(ModelMetaclass):
+    _ninja_resolvers: Dict[str, Resolver]
+
+    @no_type_check
+    def __new__(cls, name, bases, namespace, **kwargs):
+        resolvers = {}
+
+        for base in reversed(bases):
+            base_resolvers = getattr(base, "_ninja_resolvers", None)
+            if base_resolvers:
+                resolvers.update(base_resolvers)
+        for attr, resolve_func in namespace.items():
+            if not attr.startswith("resolve_"):
+                continue
+            if (
+                not callable(resolve_func)
+                # A staticmethod isn't directly callable in Python <=3.9.
+                and not isinstance(resolve_func, staticmethod)
+            ):
+                continue
+            resolvers[attr[8:]] = Resolver(resolve_func)
+
+        result = super().__new__(cls, name, bases, namespace, **kwargs)
+        result._ninja_resolvers = resolvers
+        return result
+
+
+class Schema(BaseModel, metaclass=ResolverMetaclass):
     class Config:
         orm_mode = True
         getter_dict = DjangoGetter
+
+    @classmethod
+    def from_orm(cls: Type[S], obj: Any) -> S:
+        getter_dict = cls.__config__.getter_dict
+        obj = (
+            # DjangoGetter also needs the class so it can find resolver methods.
+            getter_dict(obj, cls)
+            if issubclass(getter_dict, DjangoGetter)
+            else getter_dict(obj)
+        )
+        return super().from_orm(obj)
+
+    @classmethod
+    def _decompose_class(cls, obj: Any) -> GetterDict:
+        # This method has backported logic from Pydantic 1.9 and is no longer
+        # needed once that is the minimum version.
+        if isinstance(obj, GetterDict):
+            return obj
+        return super()._decompose_class(obj)  # pragma: no cover


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 437d54f..34ce881 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,4 +1,4 @@
-name: Test Coverage
+name: Test
 
 on:
   push:
diff --git a/ninja/testing/client.py b/ninja/testing/client.py
index 19f488e..dea6669 100644
--- a/ninja/testing/client.py
+++ b/ninja/testing/client.py
@@ -7,7 +7,7 @@ import django
 from django.http import QueryDict, StreamingHttpResponse
 
 from ninja import NinjaAPI, Router
-from ninja.responses import NinjaJSONEncoder, Response as HttpResponse
+from ninja.responses import Response as HttpResponse
 
 
 def build_absolute_uri(location: Optional[str] = None) -> str:
@@ -61,7 +61,7 @@ class NinjaClientBase:
         **request_params: Any,
     ) -> "NinjaResponse":
         if json is not None:
-            request_params["body"] = json_dumps(json, cls=NinjaJSONEncoder)
+            request_params["body"] = json_dumps(json)
         func, request, kwargs = self._resolve(method, path, data, request_params)
         return self._call(func, request, kwargs)  # type: ignore
 
@@ -123,12 +123,8 @@ class NinjaClientBase:
             request.POST = data
         else:
             request.POST = QueryDict(mutable=True)
-
-            if isinstance(data, (str, bytes)):
-                request_params["body"] = data
-            elif data:
-                for k, v in data.items():
-                    request.POST[k] = v
+            for k, v in data.items():
+                request.POST[k] = v
 
         if "?" in path:
             request.GET = QueryDict(path.split("?")[1])
diff --git a/tests/test_enum.py b/tests/test_enum.py
index 9febe5b..46a0753 100644
--- a/tests/test_enum.py
+++ b/tests/test_enum.py
@@ -88,7 +88,6 @@ def test_schema():
     assert room_param == {
         "in": "query",
         "name": "room",
-        "description": "An enumeration.",
         "required": True,
         "schema": {
             "title": "RoomEnum",
diff --git a/tests/test_openapi_schema.py b/tests/test_openapi_schema.py
index b3667c8..c168236 100644
--- a/tests/test_openapi_schema.py
+++ b/tests/test_openapi_schema.py
@@ -544,7 +544,6 @@ def test_schema_title_description(schema):
         {
             "in": "query",
             "name": "param2",
-            "description": "param 2 desc",
             "required": False,
             "schema": {
                 "default": "A Default",
diff --git a/tests/test_schema.py b/tests/test_schema.py
index fa68982..0ae5d02 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1,4 +1,4 @@
-from typing import List
+from typing import List, Optional
 from unittest.mock import Mock
 
 from django.db.models import Manager, QuerySet
@@ -34,11 +34,16 @@ class Tag:
         self.title = title
 
 
-# mocking some user:
+# mocking some users:
+class Boss:
+    name = "Jane Jackson"
+
+
 class User:
-    name = "John"
+    name = "John Smith"
     group_set = FakeManager([1, 2, 3])
     avatar = ImageFieldFile(None, Mock(), name=None)
+    boss: Optional[Boss] = Boss()
 
     @property
     def tags(self):
@@ -57,11 +62,27 @@ class UserSchema(Schema):
     avatar: str = None
 
 
+class UserWithBossSchema(UserSchema):
+    boss: Optional[str] = Field(None, alias="boss.name")
+    has_boss: bool
+
+    @staticmethod
+    def resolve_has_boss(obj):
+        return bool(obj.boss)
+
+
+class UserWithInitialsSchema(UserWithBossSchema):
+    initials: str
+
+    def resolve_initials(self, obj):
+        return "".join(n[:1] for n in self.name.split())
+
+
 def test_schema():
     user = User()
     schema = UserSchema.from_orm(user)
     assert schema.dict() == {
-        "name": "John",
+        "name": "John Smith",
         "groups": [1, 2, 3],
         "tags": [{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}],
         "avatar": None,
@@ -75,8 +96,47 @@ def test_schema_with_image():
     user.avatar = ImageFieldFile(None, field, name="smile.jpg")
     schema = UserSchema.from_orm(user)
     assert schema.dict() == {
-        "name": "John",
+        "name": "John Smith",
         "groups": [1, 2, 3],
         "tags": [{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}],
         "avatar": "/smile.jpg",
     }
+
+
+def test_with_boss_schema():
+    user = User()
+    schema = UserWithBossSchema.from_orm(user)
+    assert schema.dict() == {
+        "name": "John Smith",
+        "boss": "Jane Jackson",
+        "has_boss": True,
+        "groups": [1, 2, 3],
+        "tags": [{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}],
+        "avatar": None,
+    }
+
+    user_without_boss = User()
+    user_without_boss.boss = None
+    schema = UserWithBossSchema.from_orm(user_without_boss)
+    assert schema.dict() == {
+        "name": "John Smith",
+        "boss": None,
+        "has_boss": False,
+        "groups": [1, 2, 3],
+        "tags": [{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}],
+        "avatar": None,
+    }
+
+
+def test_with_initials_schema():
+    user = User()
+    schema = UserWithInitialsSchema.from_orm(user)
+    assert schema.dict() == {
+        "name": "John Smith",
+        "initials": "JS",
+        "boss": "Jane Jackson",
+        "has_boss": True,
+        "groups": [1, 2, 3],
+        "tags": [{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}],
+        "avatar": None,
+    }
diff --git a/tests/test_test_client.py b/tests/test_test_client.py
index c0114f9..1dcd23c 100644
--- a/tests/test_test_client.py
+++ b/tests/test_test_client.py
@@ -1,12 +1,9 @@
-from datetime import datetime
 from http import HTTPStatus
 from unittest import mock
 
 import pytest
-from django.utils import timezone
 
 from ninja import Router
-from ninja.schema import Schema
 from ninja.testing import TestClient
 
 router = Router()
@@ -64,28 +61,3 @@ def test_django_2_2_plus_headers(version, has_headers):
             request = call.call_args[0][1]
             # for Django >= 2.2 we apply a HttpHeaders instance to .headers
             assert isinstance(request.headers, mock.Mock) != has_headers
-
-
-class ClientTestSchema(Schema):
-
-    time: datetime
-
-
-def test_schema_as_data():
-    schema_instance = ClientTestSchema(time=timezone.now().replace(microsecond=0))
-
-    with mock.patch.object(client, "_call") as call:
-        client.post("/test", json=schema_instance)
-        request = call.call_args[0][1]
-        assert ClientTestSchema.parse_raw(request.body).json() == schema_instance.json()
-
-
-def test_json_as_body():
-    schema_instance = ClientTestSchema(time=timezone.now().replace(microsecond=0))
-
-    with mock.patch.object(client, "_call") as call:
-        client.post(
-            "/test", data=schema_instance.json(), content_type="application/json"
-        )
-        request = call.call_args[0][1]
-        assert ClientTestSchema.parse_raw(request.body).json() == schema_instance.json()
diff --git a/tests/test_with_django/schema_fixtures/test-multi-cookie.json b/tests/test_with_django/schema_fixtures/test-multi-cookie.json
index faac3b9..3ff92a6 100644
--- a/tests/test_with_django/schema_fixtures/test-multi-cookie.json
+++ b/tests/test_with_django/schema_fixtures/test-multi-cookie.json
@@ -25,7 +25,6 @@
       {
         "in": "cookie",
         "name": "foo4",
-        "description": "Foo4 Desc",
         "schema": {
           "title": "Foo4 Title",
           "description": "Foo4 Desc",
@@ -66,7 +65,6 @@
       {
         "in": "cookie",
         "name": "foo2",
-        "description": "Foo2 Desc",
         "schema": {
           "title": "Foo2 Title",
           "description": "Foo2 Desc",
@@ -87,7 +85,6 @@
       {
         "in": "cookie",
         "name": "foo3",
-        "description": "Foo3 Desc",
         "schema": {
           "title": "Foo3 Title",
           "description": "Foo3 Desc",
diff --git a/tests/test_with_django/schema_fixtures/test-multi-header.json b/tests/test_with_django/schema_fixtures/test-multi-header.json
index 8c1610b..f7d2c4c 100644
--- a/tests/test_with_django/schema_fixtures/test-multi-header.json
+++ b/tests/test_with_django/schema_fixtures/test-multi-header.json
@@ -25,7 +25,6 @@
       {
         "in": "header",
         "name": "foo4",
-        "description": "Foo4 Desc",
         "schema": {
           "title": "Foo4 Title",
           "description": "Foo4 Desc",
@@ -66,7 +65,6 @@
       {
         "in": "header",
         "name": "foo2",
-        "description": "Foo2 Desc",
         "schema": {
           "title": "Foo2 Title",
           "description": "Foo2 Desc",
@@ -87,7 +85,6 @@
       {
         "in": "header",
         "name": "foo3",
-        "description": "Foo3 Desc",
         "schema": {
           "title": "Foo3 Title",
           "description": "Foo3 Desc",
diff --git a/tests/test_with_django/schema_fixtures/test-multi-path.json b/tests/test_with_django/schema_fixtures/test-multi-path.json
index d549c9b..b2e7e39 100644
--- a/tests/test_with_django/schema_fixtures/test-multi-path.json
+++ b/tests/test_with_django/schema_fixtures/test-multi-path.json
@@ -25,7 +25,6 @@
       {
         "in": "path",
         "name": "foo4",
-        "description": "Foo4 Desc",
         "schema": {
           "title": "Foo4 Title",
           "description": "Foo4 Desc",
@@ -66,7 +65,6 @@
       {
         "in": "path",
         "name": "foo2",
-        "description": "Foo2 Desc",
         "schema": {
           "title": "Foo2 Title",
           "description": "Foo2 Desc",
@@ -87,7 +85,6 @@
       {
         "in": "path",
         "name": "foo3",
-        "description": "Foo3 Desc",
         "schema": {
           "title": "Foo3 Title",
           "description": "Foo3 Desc",
diff --git a/tests/test_with_django/schema_fixtures/test-multi-query.json b/tests/test_with_django/schema_fixtures/test-multi-query.json
index 7ad57b2..942c101 100644
--- a/tests/test_with_django/schema_fixtures/test-multi-query.json
+++ b/tests/test_with_django/schema_fixtures/test-multi-query.json
@@ -25,7 +25,6 @@
       {
         "in": "query",
         "name": "foo4",
-        "description": "Foo4 Desc",
         "schema": {
           "title": "Foo4 Title",
           "description": "Foo4 Desc",
@@ -66,7 +65,6 @@
       {
         "in": "query",
         "name": "foo2",
-        "description": "Foo2 Desc",
         "schema": {
           "title": "Foo2 Title",
           "description": "Foo2 Desc",
@@ -87,7 +85,6 @@
       {
         "in": "query",
         "name": "foo3",
-        "description": "Foo3 Desc",
         "schema": {
           "title": "Foo3 Title",
           "description": "Foo3 Desc",