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

Bind Python Types to GraphQL Input Types [How To] #333

Closed
Sonictherocketman opened this issue Mar 12, 2020 · 10 comments · Fixed by #1057
Closed

Bind Python Types to GraphQL Input Types [How To] #333

Sonictherocketman opened this issue Mar 12, 2020 · 10 comments · Fixed by #1057
Assignees
Labels
enhancement New feature or request roadmap Feature that we want to have included
Milestone

Comments

@Sonictherocketman
Copy link
Contributor

Perhaps it's just unclear in the docs, but I don't see any way to automatically convert from the input dictionaries given by Ariadne, to my resolvers, into custom Python types (i.e. dataclasses)

Is this supported? I found the docs on bindables, but from just that I can't tell if that's what I want.

Example

@dataclass
class Person:
    id: int
    name: str
    phone_number: str


@mutation.field('updatePerson')
def update_person(_, info, person: Person):
    # Currently what comes in is a dict. 
    # Is there a way to automatically map this to Person?
    do_update(
        person.id, 
        name=person.name, 
        phone=person.phone_number
    )
@rafalp
Copy link
Collaborator

rafalp commented Mar 12, 2020

Hello!

Thank you for asking, but issue tracker is used exclusively for tracking bugs and roadmap. We are using Spectrum for general Q&A and usage discussion.

A very similar question is already being discussed here: https://spectrum.chat/ariadne/general/how-to-access-resolver-arguments-as-custom-pydantic-any-other-objects-except-pure-dictionaries~8209983f-6e2d-4215-9976-d13d1e1acad2

@rafalp rafalp closed this as completed Mar 12, 2020
@steve-marmalade
Copy link

@rafalp - the link you provided returns a 404. Do you know whether past discussions have been archived somewhere?

@rafalp
Copy link
Collaborator

rafalp commented Feb 21, 2023

Yeah, Spectrum disappeared few months after GH bought them out.

What author asks about is not possible to do in "pure" GraphQL, and this is what vanilla Ariadne does. But it's something I'll keep on radar for Ariadne GraphQL Modules.

In the meantime you would have to cast them manually in resolver or write custom decorator. Maybe we could have this decorator as an utility in Ariadne?

@rafalp rafalp reopened this Feb 21, 2023
@rafalp rafalp added enhancement New feature or request decision needed Sounds like good idea, but will need closer scrutiny for final decision. labels Feb 21, 2023
@rafalp
Copy link
Collaborator

rafalp commented Feb 21, 2023

I'll reopen this because its something we could do in Ariadne proper, but not a high priority.

@steve-marmalade
Copy link

Thanks for the quick response @rafalp. I can see the benefit of such a utility being available in Ariadne, although to your point it's not unreasonable to write it in user-land.

In general, I think any ergonomic improvements to the relationship between the GraphQL schema and python type annotations is really powerful. While I haven't used it yet, the new ariadne-codegen package takes care of going from GraphQL schema -> pydantic models. So, adding some magic to the resolvers to make use of those models seems like a great next step.

While manually adding a decorator to a resolver is simple and safe, I'd like to briefly advocate for a "bolder" approach (more magic, but also more convenient): I think if Ariadne did some analysis of the resolver type signature at schema creation time (e.g. using inspect.get_annotations) and added this casting utility decorator automatically for any resolvers whose annotations are BaseModel or dataclass, that would be pretty delightful.

As an aside: Given the really serious performance implications of using the built-in middleware, I have followed a similar pattern for "registering" utility decorators to run on subsets of the resolvers. Again, it's always possible to add the decorator manually, but for decorators that add logging context or trace information, it gets pretty unwieldy adding multiple decorators to almost every resolver.

@rafalp
Copy link
Collaborator

rafalp commented Feb 21, 2023

Yes. Basically there's no way in GraphQL to tell query executor to represent value of argument or input field as an object other than scalar. And scalars only exist in GraphQL schemas as scalar Name, so there's no way to tell from schema what fields scalar has.

GraphQL lets us say what names should inputs and arguments values be passed to in Python, but that's it. So this conversion has to be done in the resolver, so best we could do would be to wrap user's resolver in custom resolver that does this conversion before passing the value to resolver proper.

So we would have to implement resolver decorator anyway, we would just need to have a "do_magic_args_types_conversion" option on make_executable_schema like how we already do it for cases conversion in Ariadne 0.18.

@rafalp rafalp added roadmap Feature that we want to have included and removed decision needed Sounds like good idea, but will need closer scrutiny for final decision. labels Mar 16, 2023
@rafalp
Copy link
Collaborator

rafalp commented Mar 16, 2023

I was wrong! Apparently Inputs in graphql-core now have an out_type option which is an callable called with dict with input's values and is supposed to return Python representation of input. So we can put this on the roadmap :D

@rafalp
Copy link
Collaborator

rafalp commented Mar 20, 2023

Ok, so out_type expects factory function that gets called with single argument (a dict with input data) and returns Python repr from this data.

Something like this doesn't work:

InputType("MyInput", MyDataclass)

But with small change it does:

InputType("MyInput", lambda data: MyDataclass(**data))

This opens interesting possibilities like using different Python type depending on input's contents themselves.

But I need to decide if Ariadne should funnel people to custom factory functions/lambda, or do it for them (which I am currently 90% against).

@rafalp rafalp mentioned this issue Mar 21, 2023
@rafalp rafalp added this to the Next release milestone Mar 21, 2023
@rafalp rafalp self-assigned this Mar 21, 2023
@ofer-pd
Copy link

ofer-pd commented Oct 10, 2024

That's great for customizing the out_type on a per-InputType basis, but what if you want to change the default out_type for all InputTypes at once?

For example, I have a custom class AttrDict in my codebase that makes dict items accessible as attributes, and I'd love to make Ariadne use that by default instead of plain old dict.

@rafalp
Copy link
Collaborator

rafalp commented Oct 10, 2024

You can easily write utility function that sets custom out_type on all Inputs in schema.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request roadmap Feature that we want to have included
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants