From 44547be50f0666cdcc435bd15bb89f97ca1b9613 Mon Sep 17 00:00:00 2001 From: Cam Date: Tue, 19 May 2026 17:00:09 +0800 Subject: [PATCH] fix: use JsonValue for JSONField to accept dict input Pydantic's Json type expects serialized strings, so response schemas built from Django models with JSONField fail with json_type ValidationError when the ORM returns dicts. Switch the converter for both PostgreSQL JSON/HStore fields and Django 3.1+ models.JSONField to JsonValue, which accepts JSON-compatible Python objects directly. Updated the corresponding v2 test to assert the new (correct) behavior. Refs eadwinCode/django-ninja-extra#267 --- ninja_schema/orm/utils/converter.py | 10 +++------- tests/test_v2_pydantic/test_converters.py | 11 +++-------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/ninja_schema/orm/utils/converter.py b/ninja_schema/orm/utils/converter.py index 12560ed..c287326 100755 --- a/ninja_schema/orm/utils/converter.py +++ b/ninja_schema/orm/utils/converter.py @@ -10,7 +10,7 @@ from django.db import models from django.db.models.fields import Field from django.utils.encoding import force_str -from pydantic import AnyUrl, EmailStr, IPvAnyAddress, Json +from pydantic import AnyUrl, EmailStr, IPvAnyAddress, JsonValue from pydantic.fields import Field as PydanticField from typing_extensions import Annotated # F401 @@ -452,9 +452,7 @@ def convert_postgres_array_to_list( def convert_postgres_field_to_string( field: Field, **kwargs: DictStrAny ) -> t.Tuple[t.Type, PydanticField]: - python_type = Json - if field.null: - python_type = t.Optional[Json] + python_type = JsonValue return construct_field_info(python_type, field) @@ -476,7 +474,5 @@ def convert_postgres_range_to_string( def convert_field_to_json_string( field: Field, **kwargs: DictStrAny ) -> t.Tuple[t.Type, PydanticField]: - python_type = Json - if field.null: - python_type = t.Optional[Json] + python_type = JsonValue return construct_field_info(python_type, field) diff --git a/tests/test_v2_pydantic/test_converters.py b/tests/test_v2_pydantic/test_converters.py index f19cde1..058582e 100644 --- a/tests/test_v2_pydantic/test_converters.py +++ b/tests/test_v2_pydantic/test_converters.py @@ -5,7 +5,6 @@ import pytest from django.db import models from django.db.models import Manager -from pydantic import ValidationError from ninja_schema import ModelSchema from ninja_schema.pydanticutils import IS_PYDANTIC_V1 @@ -303,6 +302,7 @@ class Config: print(ModelNewFieldsSchema.schema()) assert ModelNewFieldsSchema.schema() == { + "$defs": {"JsonValue": {}}, "properties": { "id": { "anyOf": [{"type": "integer"}, {"type": "null"}], @@ -311,11 +311,9 @@ class Config: "title": "Id", }, "jsonfield": { - "contentMediaType": "application/json", - "contentSchema": {}, + "$ref": "#/$defs/JsonValue", "description": "", "title": "Jsonfield", - "type": "string", }, "positivebigintegerfield": { "description": "", @@ -328,11 +326,8 @@ class Config: "type": "object", } - with pytest.raises(ValidationError): - ModelNewFieldsSchema(id=1, jsonfield={"any": "data"}, positivebigintegerfield=1) - obj = ModelNewFieldsSchema( - id=1, jsonfield=json.dumps({"any": "data"}), positivebigintegerfield=1 + id=1, jsonfield={"any": "data"}, positivebigintegerfield=1 ) assert obj.dict() == { "id": 1,