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

Support *args **kwargs to built in converters (e.g. pydantic by_alias) #225

Open
jkornblum opened this issue Jun 8, 2021 · 4 comments
Open

Comments

@jkornblum
Copy link

jkornblum commented Jun 8, 2021

Is your feature request related to a problem? Please describe.
Pydantic is supported out of the box but I can't use it to serialize when using field aliases.
Pydantic has support for this but I don't see a way to pass this information in Uplink.

Say I have a pydantic model like the following. The API uses camelcase but Python api should use snake case.

from pydantic import BaseModel

def to_camel(string: str) -> str:
    return "".join(word.capitalize() for word in string.split("_"))

class Case(BaseModel):
  instance_name: str
  instance_state: str

    class Config:
        alias_generator = to_camel
        allow_population_by_field_name = True

In Uplink I have

from .models import Case
from uplink import Consumer, get, post, json, returns, Body

class CaseAPI(Consumer):
    @get("scenario/{case_id}")
    def get(self, case_id: int) -> Case:
        """Get case by case_id"""

    @json
    @returns.json
    @post("scenario")
    def create(self, payload: Body(type=Case)):
        """Create a new case"""

The GET works based on the alias_generator in model but the POST does not work (does not convert snake to camel case) because one needs to pass by_alias argument to the .json() method on Pydantic model class [doc]

Describe the solution you'd like
I'd like to be able to pass *args and **kwargs to the converters that are built in. API something like

...
    @json
    @returns.json
    @post("scenario")
    def create(self, payload: Body(type=Case, by_alias=True)):
....

For now my work-around is just to use custom serialization function.

Cheers - Josh

@anentropic
Copy link

I just hit this too

it seems like uplink does not call pydantic_model.dict() like you would expect, but instead uses this other function:

from pydantic.json import pydantic_encoder

and in pydantic that method doesn't pass thru kwargs to dict()
https://github.com/samuelcolvin/pydantic/blob/6f46a5a1460728868f860840e3f6186aa40403d8/pydantic/json.py#L78

 return obj.dict()

@prkumar
Copy link
Owner

prkumar commented Jan 16, 2022

I'm looking into different approaches for how we can address this. The approach I'm favoring presently is to define a new decorator (e.g., converters.pydantic.dict_args(...)):

@json
@returns.json
@converters.pydantic.dict(by_alias=True)
@post("scenario")
def create(self, payload: Body(type=Case)):
	pass

Passing args through Body sounds fine to me, too. However, It may require more effort for similar functionality. One upside of this approach is that we could clearly specify different model.dict args per Body argument. However, I don't think such a feature would be necessary, since there should only be one @Body-annotated argument per @json-decorated method.

@sthagen
Copy link

sthagen commented Jan 16, 2022

As long as the by_alias works by really providing aliases requested here. I remember some surprising name effect collisions fro browsing through the pydantic project issue discussions after banging my head trying to hand-over snake case aliases as function arguments onto camel case model elements when in reality this was only working for pushing dicts/json through the parameter gate. But maybe that is exactly the use case here. I do not remember the name Samuel Colvin in retrospect would have preferred for the concept - I will try to find it again and update my words here, sorry for the noise.

Update: Here is my homework 🙈 pydantic/pydantic#565

@mure
Copy link

mure commented Oct 3, 2022

@jkornblum
How were you able to override the built-in Pydantic converter? I've created a custom converter, but it doesn't seem to take precedence.

edit: Ah, nevermind. I was missing the @json decorator.

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

5 participants