Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolver gets called twice #67

Open
geclick opened this issue Mar 28, 2024 · 5 comments
Open

Resolver gets called twice #67

geclick opened this issue Mar 28, 2024 · 5 comments

Comments

@geclick
Copy link

geclick commented Mar 28, 2024

I am adding a 'user' key to the token claims with the user data including its permissions. Following exactly this I get the tokens with no problem. But resolving the permissions raises a ValidationError, and I realized that the resolver gets called twice:
-first time the obj is a User instance and context is None, so no problem for getting the permission list
-the second time obj is not a User but a ModelAuthReadSchema instance and context is not None but its user is a AnonymousUser, and here comes the errors

this is my schema for User where AuthModel is just get_user_model()

class ModelAuthReadSchema(ModelSchema):
    permissions: List[str] | None

    class Meta:
        model = AuthModel
        fields = ['id', 'username', 'first_name', 'last_name', 'email']

    @staticmethod
    def resolve_permissions(obj, context):
        return get_permissions(obj)

get_permissions is a custom function for getting just business-related permissions

this is the error:

pydantic_core._pydantic_core.ValidationError: 3 validation errors for NinjaResponseSchema
response.refresh
  Field required [type=missing, input_value=<DjangoGetter: LoginInput...', username='cccccc')>, input_type=DjangoGetter]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
response.access
  Field required [type=missing, input_value=<DjangoGetter: LoginInput...', username='cccccc')>, input_type=DjangoGetter]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
response.user
  Field required [type=missing, input_value=<DjangoGetter: LoginInput...', username='cccccc')>, input_type=DjangoGetter]
    For further information visit https://errors.pydantic.dev/2.6/v/missing

this would be the desired output:

{
  "refresh": "xxxxxxxxxxx",
  "access": "yyyyyyyyyyy",
  "user": {
    "permissions": [
      "app_label1.add_model",
      "app_label2.delete_model"
    ],
    "id": 1,
    "username": "ccccccc",
    "first_name": "",
    "last_name": "",
    "email": "ccc@ccc.cc"
  }
}
@eadwinCode
Copy link
Owner

@geclick Sorry I didn't get notified of this issue on time. I am just seeing it now. I am currently looking into it at the moment

@eadwinCode
Copy link
Owner

@geclick the problem is the resolve_permission method. I don't use the resolver method provided in the Ninja model schema. The problem with that being called twice is likely a problem from pydantic. I know I faced that problem once but I can't remember where.

You can only solve this problem using pydantic model validator at mode='before'. Check the example below.

class ModelAuthReadSchema(ModelSchema):
    permissions: List[str] | None

    class Meta:
        model = AuthModel
        fields = ['id', 'username', 'first_name', 'last_name', 'email']
    
    @model_validator(mode="before")
    def validate_permission_list(cls, values: DjangoGetter) -> typing.Any:
        values = values._obj

        if isinstance(values, dict):
            # values will have ['id', 'username', 'first_name', 'last_name', 'email']
            user = None # add functionality to get the user
            permissions = get_permissions(user)
            values.update(permissions=permissions)
        return values

@geclick
Copy link
Author

geclick commented Apr 8, 2024

@geclick the problem is the resolve_permission method. I don't use the resolver method provided in the Ninja model schema. The problem with that being called twice is likely a problem from pydantic. I know I faced that problem once but I can't remember where.

You can only solve this problem using pydantic model validator at mode='before'. Check the example below.

class ModelAuthReadSchema(ModelSchema):
    permissions: List[str] | None

    class Meta:
        model = AuthModel
        fields = ['id', 'username', 'first_name', 'last_name', 'email']
    
    @model_validator(mode="before")
    def validate_permission_list(cls, values: DjangoGetter) -> typing.Any:
        values = values._obj

        if isinstance(values, dict):
            # values will have ['id', 'username', 'first_name', 'last_name', 'email']
            user = None # add functionality to get the user
            permissions = get_permissions(user)
            values.update(permissions=permissions)
        return values

tnks

@eadwinCode
Copy link
Owner

@geclick can you close this issue if the problem is resolved?

@dusty-phillips
Copy link

dusty-phillips commented Nov 22, 2024

The library is not behaving as documented. The documentation seems sane to me, so I think it's the library that's broken. But minimally, the documentation for resolvers needs to be updated: https://django-ninja.dev/guides/response/#resolvers

It says that a static resolver is called with a Django model, but it's not; it's called twice: once with a Django model and once with a Pydantic model.

The second part of that section is also incorrect; if you try to define a normal method resolver(without @staticmethod), it throws a NotImplementedError.

Edit: Just realized this is not the Django-ninja repo. 😂 But it might indicate that the upstream issue is in Django-ninja rather than Pydantic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants