litestar-org / polyfactory

Simple and powerful factories for mock data generation
https://polyfactory.litestar.dev/
MIT License
1.06k stars 83 forks source link

Bug: Pydantic Json type is disregarded when creating mock data #302

Closed Lyrete closed 1 year ago

Lyrete commented 1 year ago

Description

It seems like the pydantic_factory completely disregards a given Json type for a field if it has any nesting.

I ran into the problem today while trying to create test with a more nested model, but realized it's reproduceable with the simplest of examples.

From a small investigation, it seems that the FieldInfo.metadata parsing is lacking and is just completely disregarding the Json type if found in the metadata.

https://github.com/litestar-org/polyfactory/blob/f54f9f3c0b849a2d06bf4a5332da80ccdc31d1a9/polyfactory/factories/pydantic_factory.py#L89-L92

And I guess the parse_constrraints function itself

https://github.com/litestar-org/polyfactory/blob/f54f9f3c0b849a2d06bf4a5332da80ccdc31d1a9/polyfactory/field_meta.py#L162-L207

I contemplated trying to fix it but at least for now I don't really know how to proceed.

MCVE

from pydantic import BaseModel, Json
from polyfactory.factories.pydantic_factory import ModelFactory

class BasicTestModel(BaseModel):
    body: Json[int]

class BasicTestFactory(ModelFactory):
    __model__ = BasicTestModel

basicTest = BasicTestFactory.build()

Steps to reproduce

1. Create a Model with a Json field.
2. Create a Factory for the model.
3. Try to build a mock model with the factory.

Logs

Traceback (most recent call last):
  File ".../polyfactory-json/test.py", line 40, in <module>
    basicTest = BasicTestFactory.build()
                ^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../polyfactory/factories/pydantic_factory.py", line 310, in build
    return cls.__model__(**processed_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../pydantic/main.py", line 150, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for BasicTestModel
body
  JSON input should be string, bytes or bytearray [type=json_type, input_value=6014, input_type=int]
    For further information visit https://errors.pydantic.dev/2.1.2/v/json_type

Litestar Version

-

Platform

Lyrete commented 1 year ago

With a bigger model it will just not serialize the inner model, say like so

class TestModel(BaseModel):
    a: str

class TestBiggerModel(BaseModel):
    body: Json[TestModel]

class BiggerModelFactory(ModelFactory):
    __model__ = TestBiggerModel

testModel = BiggerModelFactory.build()

Which will result in

pydantic_core._pydantic_core.ValidationError: 1 validation error for TestBiggerModel
body
  JSON input should be string, bytes or bytearray [type=json_type, input_value=TestModel(a='TFnKscxAopHrrUFSSDMP'), input_type=TestModel]
guacs commented 1 year ago

@Lyrete you can work around this issue for now by calling the build method with factory_use_construct set to True.

class TestModel(BaseModel):
    a: str

class TestBiggerModel(BaseModel):
    body: Json[TestModel]

class BiggerModelFactory(ModelFactory):
    __model__ = TestBiggerModel

     @classmethod
    def build(cls, **kwargs: Any) -> BiggerModelFactory:
        return super().build(factory_use_construct=True, **kwargs)

testModel = BiggerModelFactory.build()

One drawback to this is that the validation upon instantiation is disabled when using this approach.