graphene
graphene copied to clipboard
There's some way to set the `source` field param for nested fields?
Context
I'm currently trying to set my model user User
subfield user.actor.id
as field source of my DjangoObjectType
.
This happens because there's other kind of users within the application like an Organization
or even a Bot
(all of them are actors).
Example
Lets define a custom Type
:
class User(DjangoObjectType):
class Meta:
model = UserModel
exclude = ['actor']
filter_fields = ['actor__login']
interfaces = [graphene.relay.Node]
skip_registry = True
# The login field is originated from `actor` relationship, not from the user model itself.
# It doesn't work, actor__login is interpreted as user field [user.actor__login] not [user.actor.login].
login = graphene.Field(graphene.String, source='actor__login')
# ...
In the example above I define the login field source as source='actor__login'
but I saw that here we're using getattr
function to resolve the source so it's currently impossible to set the field without defining a custom resolver.
https://github.com/graphql-python/graphene/blob/dfece7f65d4bad51a87a8e9f03f9acef1e25d3f8/graphene/types/field.py#L16-L20
I would like to know if there's an alternative solution to select a subfield like the example or we're forced to create a custom resolver by using the resolver=lambda _, __: ...
arg or the class method declaration.
Thanks for the hard work and the library!
I think you'll need to create a resolver returning root.actor.login/resolving the actor first depending on your setup.
Yeah, but since it's only field configuration I created a function to generate the resolver itself:
def subfield(source, separator='.'):
def resolver(self, info, *args, **kwargs):
value = self
for part in source.split(separator):
value = getattr(value, part)
return value
return resolver
def source(source, separator='.'):
return {'resolver': subfield(source, separator)}
Usage:
class User(DjangoObjectType):
class Meta:
model = UserModel
exclude = ['actor']
filter_fields = ['actor__login']
interfaces = [Node]
skip_registry = True
login = graphene.Field(graphene.String, **source('actor.login'))
# ...
Sorry for the late reply, didn't get the notification. Looks interesting! Do you want to open a PR and integrate it into the main repo?