eadwinCode / ninja-schema

Ninja Schema is Pydantic full support for Django ORM.
MIT License
41 stars 1 forks source link

orm.factory _get_schema_v2 always returns None in my env #13

Open sebcampos opened 3 days ago

sebcampos commented 3 days ago

running Debian GNU/Linux on a google cloud instance. Python version: Python 3.13.0+

I was using django-ninja-extra which allows to create a model schema by leveraging ninja-schema in the following way:

@api_controller("/pypi")
class PythonPackageModelController(ModelControllerBase):
    model_config = ModelConfig(
            model=PythonPackage,
            schema_config=ModelSchemaConfig(read_only_fields=["id"]),
            async_routes=True,
            pagination=ModelPagination(
                klass=LimitOffsetPagination,
                pagination_schema=NinjaPaginationResponseSchema
            )

But ninja schema kept throwing and error in which it was trying to get the name attribute from a NoneType.

Upon looking into the form/factory.py file I saw that the code was eventually calling this line:

        new_schema = (
            cls._get_schema_v1(name, model_config_kwargs, ModelSchema)
            if IS_PYDANTIC_V1
            else cls._get_schema_v2(name, model_config_kwargs, ModelSchema)
        )

In my case cls._get_schema_v2 was returning None.

I added a breakpoint at cls._get_schema_v2:

    def _get_schema_v2(
        cls, name: str, model_config_kwargs: typing.Dict, model_type: typing.Type
    ) -> Union[Type["ModelSchema"], Type["Schema"]]:
        model_config = cls.get_model_config(**model_config_kwargs)
        new_schema_string = f"""class {name}(model_type):
            class Config(model_config):
                pass """
        exec(new_schema_string, locals())
        breakpoint()
        return locals().get(name)

I noticed that the exec statement was not storing the new object it create in locals, therefore locals().get(name) always returned None.

But with this change:

     def _get_schema_v2(
        cls, name: str, model_config_kwargs: typing.Dict, model_type: typing.Type
    ) -> Union[Type["ModelSchema"], Type["Schema"]]:
        model_config = cls.get_model_config(**model_config_kwargs)
        res = {}
        new_schema_string = f"""class {name}(model_type):
            class Config(model_config):
                pass """
        exec(new_schema_string, locals(), res)
        return res.get(name)  # type:ignore[return-value]

I am getting the expected result

Screenshot 2024-10-26 at 2 27 29 PM

Original code with breakpoint: Screenshot 2024-10-26 at 2 30 12 PM

after change: Screenshot 2024-10-26 at 2 32 35 PM