vitalik / django-ninja

💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
https://django-ninja.dev
MIT License
7.25k stars 431 forks source link

[BUG] PatchDict errors with inherited schemas #1324

Open filippomc opened 3 weeks ago

filippomc commented 3 weeks ago

Describe the bug

I have a schema hierarchy such as:

class ViewableContent(Schema):
    name: str
    description: str = None

class MySchema(ViewableContent):
   other: str # If I don't add a new field the problem does not arise

Then add a router like the following:

@router.patch('/{uuid}', response={200: MySchema})
@transaction.atomic
def my_update(request: HttpRequest, uuid: str, payload: PatchDict[MySchema]):
   ...

When I run my application the following error is raised:

  File "/home/user/mnp/applications/neuroglass-research/backend/neuroglass_research/api/__init__.py", line 4, in <module>
    from .studies import router as studies_router
  File "/home/user/mnp/applications/neuroglass-research/backend/neuroglass_research/api/studies.py", line 69, in <module>
    def update_study(request: HttpRequest, study_id: int, payload: PatchDict[UpdateStudyPayload]):
                                                                   ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "/home/user/miniconda3/envs/mnp/lib/python3.12/site-packages/ninja/patch_dict.py", line 45, in __getitem__
    new_cls = create_patch_schema(schema_cls)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/miniconda3/envs/mnp/lib/python3.12/site-packages/ninja/patch_dict.py", line 29, in create_patch_schema
    t = schema_cls.__annotations__[f]
        ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
KeyError: 'name'

Versions:

filippomc commented 3 weeks ago

Possibly related discussion: https://github.com/pydantic/pydantic/discussions/4242

vitalik commented 3 weeks ago

If I don't add a new field the problem does not arise

so problem only appears when you ADD some field ? or always ?

filippomc commented 2 weeks ago

If I don't add a field it's like Pydantic is considering the two classes equivalent and what I see is that the annotations are the same from the base class. When adding a field, that field is the only one within the annotations dict.

vitalik commented 2 weeks ago

well I still do not understand you.. can you show two examples working and non working or something...

filippomc commented 2 weeks ago

The not working example is the one I originally posted, and the application breaks on any path with the error above. These are a few ones that are working for me:

Working -- no inheritance:

class MySchema(Schema):
   name: str
   description: str = None
   other: str

Working -- duplicate all base class fields

class ViewableContent(Schema):
   name: str
   description: str = None

class MySchema(ViewableContent):
   name: str = None
   description: str = None
   other: str 

Working -- subclassing but no new fields

class ViewableContent(Schema):
   name: str
   description: str = None

class MySchema(ViewableContent):
   pass

Created an example project with all the examples