graphene-django icon indicating copy to clipboard operation
graphene-django copied to clipboard

Model inheritance in combination with graphene.Union and DjangoOrderedFilterConnection

Open piccarsso opened this issue 3 years ago • 2 comments

For a project I need to retrieve a list of videos from a database, which can be of different types. I need the possibility of filtering and pagination. The model uses inheritance (in this case with django_model_utils and InheritanceManager). So I have a BaseLearnVideo and derived from it e.g. MusicLearnVideo, DrawingLearnVideo.

For example, if I have the following models:

    class BaseLearnVideo():
      objects = InheritanceManager()
      title = models.CharField('Title, max_length=200, blank=False)
    
    class MusicLearnVideo(BaseLearnVideo)
      something_special = models.CharField('Something special', max_length=200, blank=False)
    
    class DrawingLearnVideo(BaseLearnVideo)
      cover_image = models.ImageField('Cover image')

I would like to query all learning videos as a list, a mixed list of the types MusicLearnVideo and DrawingLearnVideo. Therefore, graphene.Union would fit.

So in my schema I have:

    class MusicLearnVideoType(DjangoObjectType):
        class Meta:
            model = MusicLearnVideo
            fields = ('id', 'something_special')
            interfaces = (gql.Node,)
    
    class DrawingLearnVideoType(DjangoObjectType):
        class Meta:
            model = DrawingLearnVideo
            fields = ('id', 'cover_image')
            interfaces = (gql.Node,)
    
    class AllLearnVideosUnion(graphene.Union):
        class Meta:
            types = (MusicLearnVideoType, DrawingLearnVideoType)

  class AllLearnVideosConnection(graphene.Connection):
    class Meta:
        node = AllLearnVideosUnion

It works with graphene.Connection:

    all_learn_videos = graphene.ConnectionField(AllLearnVideosConnection)

    def resolve_all_learn_videos(self, info, **kwargs):
        return BaseLearnVideo.objects.all().select_subclasses()

As there can be a lot of different videos, I don't want to query them all, but have a pagination and filters. What I'd like to make is something like:

    all_learn_videos = DjangoFilterConnectionField(AllLearnVideosUnion)

    def resolve_all_learn_videos(self, info, **kwargs):
        return BaseLearnVideo.objects.all().select_subclasses()

The query:

    {
    edges {
      node {
        __typename
        ... on MusicLearnVideoType {
          id
          somethingSpecial
        }
        ... on DrawingLearnVideoType {
          id
         coverImage
        }
      }
    }
  }

I thought of resolving the queryset beforehand, and addressing the filters and pagination in the resolve function of the query, but as there already is the DjangoFilterConnectionField it would be nice to use that :-) I am not sure if this is even possible from a technical point of view, as I have not gone deep into the code of graphene-django.

Thanks a lot in advance for your thoughts on that!

piccarsso avatar Feb 09 '22 08:02 piccarsso

I found that the derived classes will be available in the query of the base class, so one could simply just query the base class and then access the child classes, like:

{
    edges {
      node {
        id
        musiclearnvideo {
          somethingSpecial
       }
       drawinglearnvideo {
         coverImage
      }
      }
    }
  }

I think this solves my problem.

piccarsso avatar Feb 09 '22 09:02 piccarsso

Hi! I just encountered a similar problem, where I am trying to merge two querysets from different models, but still want to be able to use the filtering and pagination functionality from DjangoFilterConnectionField. I think it would work for my case to define a superclass for the two models, but I'm curious as to how you defined the superclass in the end; did your BaseLearnVideo extend django's Model?

DrumsnChocolate avatar Apr 01 '22 14:04 DrumsnChocolate

Using DjangoFilterConnectionField with Unions and Interfaces is much needed.

pfcodes avatar Jul 08 '23 00:07 pfcodes