graphene-pydantic icon indicating copy to clipboard operation
graphene-pydantic copied to clipboard

how to pass in a json data struct?

Open c-xlenz opened this issue 1 year ago • 2 comments

I have the following challenge:

I am getting data from a database in json format / as dict. This works great when I am directly grabbing the data in the query and "upgrading" it to the defined and in meta class registered pedantic data model.

Example:

class GrapheneModel(PydanticObjectType):
    class Meta:
        model = PydanticModel

...
class Queries(ObjectType):
    examples = List(
        GrapheneModel
    )

    @staticmethod
    def resolve_examples(root, info, **kwargs):
        result = fetch_from_db() # return a list of dicts
        return [PydanticModel.model_validate(q) for q in result]

But I also have other cases where I come through an interface. I can overlay the is_type_of method to get to the right GrapheneModel, but the data that comes in is then a dict, which does not work... Do you guys have an idea how the data can be upgraded within the Graphene Model? I cannot do it in the interface as I do not know the type there... Thanks for any advise.

c-xlenz avatar Mar 01 '24 09:03 c-xlenz

Thanks for your question! It looks like above you are creating PydanticModel instances resolve_examples method, but then it's returning dicts? That is definitely unexpected!

Without seeing more of your code, I'm finding it hard to give advice. Can you show where you're overriding is_type_of? Is it in the GrapheneModel definition? Can you show sample GraphQL queries that you want to run?

necaris avatar Mar 05 '24 03:03 necaris

Thanks for your reply. I will try to sketch a bit more code around my use case so that it might be better understandable ;)

I have a interface definition like this:

class DataInterface(Interface):
    id = String()

and a generic query endpoint

class Data(ObjectBase):
    data = Field(DataInterface)

class Queries(ObjectType):
    get_data = Field(
        Data,
        id=String(),
        description="Get the data for id",
    )

 @staticmethod
    def resolve_get_data (root, info, id, **kwargs):
        result = fetch_from_db(
            id
        )
        return result # this is json

So now back to the Rest... adapted from my previous post

class PydanticModel(pydantic.BaseModel):
    id: str
    something: str


class GrapheneModel(PydanticObjectType):
    class Meta:
        model = PydanticModel
        interfaces = (DataInterface,)


class Queries(ObjectType):
    examples = List(
        GrapheneModel
    )

    @staticmethod
    def resolve_examples(root, info, **kwargs):
        result = fetch_from_db() # return a list of dicts
        return [PydanticModel.model_validate(q) for q in result]

The query that works is

query {
     examples {
         id
         something
    }
}

What is not working is the following query:

query {
     get_data(id="1" {
         data {
             ... on GrapheneModel {
                 id
                 something
          }
       }
    }
}

One issue is that the GrapheneModel is_of_type from the PydanticGraphene base class is not hitting. I could add something like this: Note: I know that this will also cause problems to resolve the Model for the direct query case...but this can be handled. this is just for demonstration....

class GrapheneModel(PydanticObjectType):
    class Meta:
        model = PydanticModel
        interfaces = (DataInterface,)

     @classmethod
    def is_type_of(cls, root, info):
        # dummy to check if we should use this
        return ["1", "2", 3"].includes(root.get("id"))

Then the right Model (GrapheneModel) gets hit but the resulting values are "null". But in any case the data comes in as dict and does not get "upgraded" to the correct model...

I hope this makes it more clear ;) Thanks in advance

c-xlenz avatar Mar 05 '24 12:03 c-xlenz