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

Django: Make m2m through table fields available on edges #83

Closed
adamcharnock opened this issue Jan 19, 2016 · 12 comments
Closed

Django: Make m2m through table fields available on edges #83

adamcharnock opened this issue Jan 19, 2016 · 12 comments
Labels

Comments

@adamcharnock
Copy link
Contributor

When a DjangoConnectionField traverses a many-to-many field it would be nice to have the option to expose the fields of any through-table on the edges of the relationship.

[As much a note for myself as a feature request]

@syrusakbary
Copy link
Member

Never thought about that but makes sense 👍

@matclayton
Copy link

We'd also love to see this, an example of using m2m relationships would also be fantastic.

@adamcharnock
Copy link
Contributor Author

@matclayton FWIW I'm currently treating the through table as simply another node. It makes the GraphQL somewhat nested, but it works for now.

@tom-zeit
Copy link

tom-zeit commented Aug 4, 2016

We would also like to see this feature :)

@jkimbo
Copy link
Member

jkimbo commented Feb 17, 2018

Hi @adamcharnock . We're currently going through old issues that appear to have gone stale (ie. not updated in about the last 6 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 Feb 17, 2018
@syrusakbary syrusakbary reopened this Jun 22, 2018
@syrusakbary
Copy link
Member

Reopening the issue as it might be useful to have

@stale
Copy link

stale bot commented Jul 29, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jul 29, 2019
@stale stale bot closed this as completed Aug 5, 2019
@McPo
Copy link

McPo commented Sep 11, 2019

Is it possible to keep this on the radar? It would be a very nice feature (And its how I assume it would work)

@McPo
Copy link

McPo commented Sep 12, 2019

So I've found a way of implementing this using annotations

class Member(DjangoObjectType):
     class Meta:
         model = models.Member

     channels = graphene.ConnectionField('api.graphql.MemberToChannelConnection')

     def resolve_channels(instance, info):
         return instance.channels.annotate(broadcaster=F('memberships__broadcaster'))

class Channel(DjangoObjectType):
     class Meta:
         model = models.Channel

     members = graphene.ConnectionField('api.graphql.ChannelToMemberConnection')

    def resolve_members(instance, info):
        return instance.members.annotate(broadcaster=F('channel_memberships__broadcaster'))


class ChannelMembershipEdge:
    broadcaster = graphene.NonNull(graphene.Boolean)

    def resolve_broadcaster(instance, info):
        return instance.node.broadcaster

class ChannelToMemberConnection(graphene.Connection):
    class Meta:
        node = Member
    class Edge(ChannelMembershipEdge):
        pass

class MemberToChannelConnection(graphene.Connection):
    class Meta:
        node = Channel
    class Edge(ChannelMembershipEdge):
        pass

I have also created a custom connection field to add annotations

from functools import partial
class AnnotateConnectionField(graphene.ConnectionField):
    def __init__(self, type, annotate_fields, *args, **kwargs):
        super().__init__(type, *args, **kwargs)
        self.annotate_fields = annotate_fields

    def get_resolver(self, parent_resolver):
        resolver = super(graphene.ConnectionField, self).get_resolver(parent_resolver)
        patched_resolver = lambda *args, **kwargs: resolver(*args, **kwargs).annotate(**self.annotate_fields)
        return partial(self.connection_resolver, patched_resolver, self.type)

So the above becomes

class Member(DjangoObjectType):
     class Meta:
         model = models.Member

     channels = AnnotateConnectionField('api.graphql.MemberToChannelConnection', dict(broadcaster=F('memberships__broadcaster')))

class Channel(DjangoObjectType):
     class Meta:
         model = models.Channel

     members = AnnotateConnectionField('api.graphql.ChannelToMemberConnection', dict(broadcaster=F('channel_memberships__broadcaster')))

class ChannelMembershipEdge:
    broadcaster = graphene.NonNull(graphene.Boolean)

    def resolve_broadcaster(instance, info):
        return instance.node.broadcaster

class ChannelToMemberConnection(graphene.Connection):
    class Meta:
        node = Member
    class Edge(ChannelMembershipEdge):
        pass

class MemberToChannelConnection(graphene.Connection):
    class Meta:
        node = Channel
    class Edge(ChannelMembershipEdge):
        pass

I also attempted to have the two Connection classes autogenerated within the connection field. So you basically pass in the the node value and it would generate the class for you. However because graphene.Connection.Meta.node does not support lazy loading the class, I got into an issue with circular dependencies that I was unable to resolve. Im sure theres a lot more magic that could be done too. One potential issue with this approach is that I doubt it will work if the Through tables "metadata" is a FK to another object, as Im not sure annotation would support that.

Anyway this is a sufficient enough solution for myself for now

@Eraldo
Copy link

Eraldo commented Dec 18, 2019

@McPo What would it take for graphene to automatically add the through model fields?

@Eraldo
Copy link

Eraldo commented May 23, 2020

Any updates on this?
Still looking forward to automatic through edges. :)

@tony
Copy link

tony commented Jan 11, 2022

@syrusakbary Can you reopen and move this to a discussion?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants