Skip to content

How to access arguments from higher levels #378

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

Closed
jwfehr opened this issue Nov 28, 2016 · 17 comments
Closed

How to access arguments from higher levels #378

jwfehr opened this issue Nov 28, 2016 · 17 comments

Comments

@jwfehr
Copy link

jwfehr commented Nov 28, 2016

Ex query
User(id: 1234) { userIsTeamLeader(teamed: 6) }

How would I access the user id in the resolve_userIsTeamLeader function? I am aware of contexts but that is not what I am looking for in this case. I want to access a higher level argument. Is this possible?

@angieellis
Copy link

I've been trying to find a solution to this myself. The only way I've found is through the info parameter in the resolve method. That holds all the arguments, models, and requested fields in the query. However, its a bit messy to go that route because you have to parse through it. Would be great to have a cleaner/easier way to do this.

@jwfehr
Copy link
Author

jwfehr commented Dec 1, 2016

Is there any documentation for the info object and how to parse through it? or can someone point me to the code where it is formed?

@yfilali
Copy link

yfilali commented Dec 1, 2016

@syrusakbary showed an example of that here: #348 (comment)


    def get_type(_type):
        if isinstance(_type, (GraphQLList, GraphQLNonNull)):
            return get_type(_type.of_type)
        return _type

    def get_fields(info):
        fragments = info.fragments
        field_asts = info.field_asts[0].selection_set.selections
        _type = get_type(info.return_type)

        for field_ast in field_asts:
            field_name = field_ast.name.value
            if isinstance(field_ast, FragmentSpread):
                for field in fragments[field_name].selection_set.selections:
                    yield field.name.value
                continue

            yield field_name

Also, for the original question, by the time you get to resolve_userIsTeamLeader, I am assuming you have already a resolved user in which case the resolve_userIsTeamLeader has access to the parent user object. Maybe try something like this?

    def resolve_userIsTeamLeader(self, args, context, info):
        print(self.id)

@jwfehr
Copy link
Author

jwfehr commented Dec 11, 2016

Apologies for the delay, I am continuing to work this problem and am having issues. When I look at the field_asts for the info object in my user class I can see my teamID argument as part of the selection set which includes userIsTeamLeader, however when I look at the field_asts for the info object in my resolve_userIsTeamLeader function the only field that is present is the userIsTeamLeader with its argument, is there any way to access the user field_asts from within my resolve_userIsTeamLeader function?

@angieellis
Copy link

I'm sorry. That only works the other way around, when you are trying to access child arguments from the parent. In that case, you should go with what @yfilali demonstrated with self. You should have the first part of the argument resolved in self with the User object. So you should be able to access the id field from there.

@angieellis
Copy link

@jwfehr I looked at this again today. You can access the parent arguments in the info object, but not in the field_asts attribute. Try the operation attribute (info.operation). That seems to have all arguments in both contexts.

@jwfehr
Copy link
Author

jwfehr commented Dec 16, 2016

@angieellis Thanks very much for the help, I will take a look at that.

@nylocx
Copy link

nylocx commented Jul 26, 2017

Hi, I have the same problem and I can't get the self solution to work.
I tried to add a minimal example to the playground at:
https://goo.gl/gd1CgM

Do I really have to parse the whole info object just to get to the filter value?

I found a workaround https://goo.gl/BqMtjV
But I don't really like it.

@Zevgon
Copy link

Zevgon commented Dec 12, 2017

For anyone else who thinks that the above solutions are too hard, my workaround was to add the arguments to info.context in the higher level resolve method. Not pretty, but very easy.

@pcattori
Copy link

@Zevgon not sure exactly how to "add arugments to info.context". could you provide an example?

@pcattori
Copy link

pcattori commented Dec 16, 2017

maybe like this?

def resolve_parent(self, info, arg):
        info.context.args = dict(arg=arg)
        return ChildType()

...

class Child(graphene.ObjectType):
    some_field = ...
    def resolve_some_field(self, info):
        return info.context.args.get('arg')

though I wish something like this would be possible, to make namespacing easier:

def resolve_parent(self, info, arg):
        return ChildType(arg=arg)

...

class Child(graphene.ObjectType):
    def resolve_some_field(self, info):
        return self.args.get('arg')

@Zevgon
Copy link

Zevgon commented Dec 18, 2017

@pcattori Yes, your first example is exactly what I mean. About your second example, I think that's possible if you access arg directly from self? Pretty sure this works:

def resolve_parent(self, info, arg):
        return ChildType(arg=arg)

...

class Child(graphene.ObjectType):
    def resolve_some_field(self, info):
        return self.arg

Although I still prefer not doing that type of thing in cases where the child inherits from DjangoObjectType because of the possibility of arg clashing with one of the model's property names.

@ekampf
Copy link
Contributor

ekampf commented Dec 19, 2017

Though using the context solution usually work it has some edge cases when runnning resolvers in parallel.
For example:

{
   product1: product(id: "1") { id, name, ...}
   product2: product(id: "2") { id, name, ...}
}

if resolve_product sets something in context, say context['current_product'] = self then it might be a problem...

@jkimbo
Copy link
Member

jkimbo commented Mar 17, 2018

Hi @jwfehr . We're currently going through old issues that appear to have gone stale (ie. not updated in about the last 3 months) to try and clean up the issue tracker. If this is still important to you please comment and we'll re-open this.

Thanks!

@jkimbo jkimbo closed this as completed Mar 17, 2018
@Zik42
Copy link

Zik42 commented Jun 1, 2019

if resolve_product sets something in context, say context['current_product'] = self then it might be a problem...

Have you found another, better way, that works with DjangoObjectType?

@McPo
Copy link

McPo commented Sep 6, 2019

Thanks @Zevgon this works, however it seems like a hack. Is there any reason why this isn't possible without the workaround.

My use case consists of a Member who has a connection with other Members (If they share a Circle then they are considered Contacts). I would like to annotate the edges of the connection with meta information as to how the two Members are linked (Via which Circles).

It seems odd to me that an Edge doesn't have access to both nodes, but only one. As such I had to do the workaround that @Zevgon has proposed. Also any additional information added to an Edge, is surely gonna have to be based off some relationship between the two nodes?

Is this likely to cause any issues?

class Member(DjangoObjectType):
    class Meta:
        model = models.Member
        filter_fields = []
        interfaces = (graphene.Node, )

    contacts = graphene.ConnectionField('api.graphql.Contact')

    def resolve_contacts(instance, info):
        info.context.root = instance
        return instance.contacts

class Contact(graphene.Connection):

    class Meta:
        node = Member

    class Edge:
        circles = graphene.List(Circle)

        def resolve_circles(instance, info):
            return info.context.root.circles.intersection(instance.node.circles.all())

Also @ZikDead it appears to work fine with for me with DjangoObjectType

  • Although this is slightly different that what OP stated, I want the node/model, not the args.

@niwla23
Copy link

niwla23 commented Aug 25, 2021

Hi @jwfehr . We're currently going through old issues that appear to have gone stale (ie. not updated in about the last 3 months) to try and clean up the issue tracker. If this is still important to you please comment and we'll re-open this.

Thanks!

this should be reopened since there is no non-hacky solution to this problem yet. I am facing the same problem rn.

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