-
-
Notifications
You must be signed in to change notification settings - Fork 535
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
Implement explicit interface type resolution #1406
Implement explicit interface type resolution #1406
Conversation
Thanks for adding the Here's a preview of the changelog: This releases fixes an issue where you were not allowed Here's the preview release card for twitter: Here's the tweet text:
|
4b06434
to
a7c96fa
Compare
I've just rebased/force pushed to resolve some conflicts with |
Codecov Report
@@ Coverage Diff @@
## main #1406 +/- ##
=======================================
Coverage 98.08% 98.09%
=======================================
Files 119 119
Lines 4129 4143 +14
Branches 599 604 +5
=======================================
+ Hits 4050 4064 +14
Misses 42 42
Partials 37 37 |
@AlecRosenbaum thanks! does this work for unions too? I'll take a proper look during the weekend 😊 |
It does now 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! ✨ I've left a few comments. Before merging this I'd like to get @BryceBeagle and @jkimbo's opinions on the API:
with this PR we allow to pass a classmethod is_type_of
like so:
@strawberry.type
class Anime(Node):
name: str
@classmethod
def is_type_of(cls, obj, _info) -> bool:
return isinstance(obj, AnimeORM)
which is a bit different that what we do at the moment, I mean, at the moment
we pass all the configuration options to @strawberry.type
.
I'm fine with having is_type_of
being a classmethod, but I want to see what you all think :)
@AlecRosenbaum we should add some documentation too :)
@@ -295,11 +279,23 @@ def get_graphql_fields() -> Dict[str, GraphQLField]: | |||
|
|||
return graphql_fields | |||
|
|||
is_type_of: Optional[Callable[[Any, GraphQLResolveInfo], bool]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GraphQLResolveInfo
<- we should use StrawberryInfo, not sure we can do this easily here though, maybe we can do it after we do this issue: #1425
Co-authored-by: Patrick Arminio <[email protected]>
If we're having users implement a function to indicate duck typing is possible... why not just have them implement a function that does the cast/conversion instead? I'm not sure we want to go this deep into supporting duck-typing |
This is something I used to do at my previous job, conversion requires a lot of typing and I don't think it's worth the trouble in all cases, if you think a strawberry object as a protocol it would make sense being allowed to return any type that conform the protocol :) Casting might be an option, but I don't really know how we can do that (other than using some hacks). The other issue with it is that you'd require to do it in every resolver. We already support duck typing by the way, it's just not well supported when using interfaces/unions (since GraphQL needs to know the actual return type) |
@patrick91 Can you give an example of this? In my experience conversion doesn't require any extra typing. |
Oh I meant writing code, not type hints :D @strawberry.type
class User:
name: str
@classmethod
def from_model(cls, model) -> User:
return User(name=model.name) imagine this with 10 fields :) |
Ok yeah that does make sense. IMO I still prefer that to duck typing but I can appreciate why it's painful. Separately I've just discovered that graphql-core will check for a Could that be a better way of dealing with this? |
I feel like there might be cases where you're dealing with dictionaries coming from something like mongodb or an API. I don't think we should force people to convert them to instances of Strawberry objects. In addition to that there's also other usecases, like getting partial objects. For example: @strawberry.type
class User:
name: str
age: int
@strawberry.type
class Query:
@strawberry.field
def user(self, info: Info) -> User:
selected_fields = fields_from_info(info)
return models.User.first().only(selected_fields) This can't be done at all with plain Strawberry Objects, at least for now 😊 That's why I think we should allow duck typing, especially to people that know what they are doing :)
This is neat! Didn't know that 😊 not sure it would be the solution, as it still requires doing some work in the resolvers, see this ugly example here: @strawberry.type
class Query:
@strawberry.field
def user(self, info: Info) -> SomeInterface:
items = SomePolymorficModel.all()[:10]
return add_type_name_to_each_item(items) I think having a Sorry if it looks like I'm insisting on this, I'm not 100% liking the idea of having to defined a classmethod on object types, but I'm trying to see it as a potential user of strawberry, and I wouldn't be happy to force to convert compatibile types to other types 😊 |
Dictionaries are one thing, but I don't think we should be encouraging duck typing between objects between separate libraries (e.g. an ORM and Strawberry). This seems incredibly fragile and prone to breakage/unexpected failure. Feels way too javascript-y to me. At minimum, I think we should have a "strict" mode toggle that disables/enables this loose (and imo dangerous) behavior. |
what makes a dictionary special? I'd argue it is better to support duck typing rather than supporting dictionaries :)
We don't encourage, but we shouldn't disallow it. People will want to be able to do that, and again I think we should treat GraphQL types are protocols more than strict classes.
We should make errors clearer then, type checkers already help with this :) I've personally used duck typing without any issue
Would this run isinstance on all returned values? |
What are the next steps for this PR then? I'm happy to implement an alternative solution as long as it would address the issue of resolving interfaces on duck-typed objects. At the very least, not supporting this would be a big pain point in our gradual transition away from |
I'm happy for this to be merged. I still have reservations about the api but I also don't have a better solution so we should merge it since it's blocking @AlecRosenbaum |
Edit: I did misunderstand the code :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for doing this PR, and sorry it took a while to get it merged 😊
Thanks for contributing to Strawberry! 🎉 You've been invited to join You can also request a free sticker by filling this form: https://forms.gle/dmnfQUPoY5gZbVT67 And don't forget to join our discord server: https://strawberry.rocks/discord 🔥 |
Description
The behavioral changes included in this PR are:
a) duck typing on fields that return interfaces or types implementing an interface now requires an explicit
is_type_of
opt-inb) adding an explicit
is_type_of
opt-in is now implemented with ais_type_of
classmethodThis seems like it might require a change to the documentation mentioning the classmethod, which I can make assuming this approach looks good.
Also, what release type should this be? Technically it is a breaking change, though it's quite minor.
Types of Changes
Issues Fixed or Closed by This PR
Checklist
I did dev work within a codespace based on your template (which was very convenient), so at the very least all the pre-commit scripts ran correctly. I also ran the test suite, which passes.