vitalik / django-ninja

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

[BUG] CountryField from django-countries and pydantic #554

Open migratis opened 2 years ago

migratis commented 2 years ago

A model using the CountryField() from django-countries if it is defined with ModelSchema to be used as a response, drive to this error :

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ninja_extra/operation.py", line 190, in run
    _processed_results = self._result_to_response(
  File "/usr/local/lib/python3.9/site-packages/ninja/operation.py", line 196, in _result_to_response
    result = response_model.from_orm(resp_object).dict(
  File "/usr/local/lib/python3.9/site-packages/ninja/schema.py", line 164, in from_orm
    return super().from_orm(obj)
  File "pydantic/main.py", line 579, in pydantic.main.BaseModel.from_orm
pydantic.error_wrappers.ValidationError: 1 validation error for NinjaResponseSchema
response -> country
  str type expected (type=type_error.str)

here is how I define the schema and use it:

from ninja_extra import NinjaExtraAPI
from ninja_jwt.controller import NinjaJWTDefaultController
from ninja_jwt.authentication import JWTTokenUserAuth
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
from ninja import ModelSchema
from myapp.user import models

api = NinjaExtraAPI(csrf=True)

class UserSchemaOut(ModelSchema):

    class Config:
        model = models.User
        model_fields = ['email', 'first_name',
            'last_name', 'country', 'language', 'birthdate']

@api.get('/getprofile', auth=JWTTokenUserAuth(), response=UserSchemaOut)
def getProfile(request):
        user = get_object_or_404(models.User, id= request.user.id)
    if user is not None:
        return user

    return JsonResponse({"user": ["user-not-exists"]}, status=422)  

api.register_controllers(
    NinjaJWTDefaultController,
)
jonas87 commented 11 months ago

@vitalik could this be something django-countries has to implement on their end?

Im getting the same problem when adding a CountryField() from django-countries (added to custom user model) If you use this schema in a response you'll get an error:

File "pydantic/main.py", line 579, in pydantic.main.BaseModel.from_orm
pydantic.error_wrappers.ValidationError: 1 validation error for NinjaResponseSchema
response -> country
str type expected (type=type_error.str)

Minimal example:

from ninja import ModelSchema
from django.contrib.auth import get_user_model

class UserOutSchema(ModelSchema):
    class Config:
        model = get_user_model()
        model_fields = [
            "email",
            "country",
        ]

And if you add the Country type from django_countries to the schema it will throw an error that an "Object of type Country is not JSON serializable":

from django_countries.fields import Country

class UserOutSchema(ModelSchema):
    country: Country

    class Config:
        arbitrary_types_allowed = True
        model = get_user_model()
        model_fields = [
            "email",
            "country",
        ]
schuem commented 4 months ago

The Country field is not just a string but a Country object with multiple attributes. Hence the user model is nested. You need to define a Country schema first which then can be used in the User schema like it's stated in the docs for overriding fields: https://django-ninja.dev/guides/response/django-pydantic/?h=modelschema#modelschema