4

I have some django model generic relation fields that I want to appear in graphql queries. Does graphene support Generic types?

class Attachment(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    file = models.FileField(upload_to=user_directory_path)
class Aparto(models.Model):
    agency = models.CharField(max_length=100, default='Default')
    features = models.TextField()
    attachments = GenericRelation(Attachment)

graphene classes:

class ApartoType(DjangoObjectType):
    class Meta:
        model = Aparto
class Query(graphene.ObjectType):
    all  = graphene.List(ApartoType)
    def resolve_all(self, info, **kwargs):
        return Aparto.objects.all()

schema = graphene.Schema(query=Query)

I expect the attachments field to appear in the graphql queries results. Only agency and features are showing.

2 Answers2

5

You need to expose Attachment to your schema. Graphene needs a type to work with for any related fields, so they need to be exposed as well.

In addition, you're likely going to want to resolve related attachments, so you'll want to add a resolver for them.

In your graphene classes, try:

class AttachmentType(DjangoObjectType):
    class Meta:
        model = Attachment

class ApartoType(DjangoObjectType):
    class Meta:
        model = Aparto

    attachments = graphene.List(AttachmentType)
    def resolve_attachments(root, info):
        return root.attachments.all()
Chris Larson
  • 1,684
  • 1
  • 11
  • 19
  • 1
    This is not the answer I am looking for. How do you expose `content_object` from the `Attachment` class? Let's say the different related objects have different fields. How to dynamically alter the ObjectType depending on the class of the returned related object? – smoquet Oct 07 '21 at 13:08
  • I achieve this trick by adding extra Charfield in Attachment class. `related_object_class = models.CharField(max_length=255, blank=True, null=True)` Given an object instance named obj, it is then sufficient to do: `related_object_class = obj.__class__.__name__` – Wilfried FUTCHEA Jun 15 '22 at 17:16
1

It's not perfect, but this is how I did it: First create a proxy class (I found that abstract = True also works), this should have all the fields that all the possible Generically Related objects can have.

class CatchAll(RelatedModelYouInheritFrom):
    class Meta:
        proxy = True

Then make a type for the proxy model

class CatchAllType(DjangoObjectType):
    class Meta:
        model = CatchAll
        fields = ('some_var', 'other_var')

and in the resolver that returns instances of multiple classes: cast the instance as a CatchAll:

class ModelWithGenericForeignKeyType(DjangoObjectType):

    class Meta:
        model = ModelWithGenericForeignKey
        fields = ('other_var', 'some_var')

    generic_relation = graphene.Field(CatchAllType)


    def resolve_generic_relation(self, info, **kwargs):
        d = self.generic_relation.__dict__  # turn the model in a dict
        d.pop('_state')  # remove the state
        return CatchAll(**d)  # cast the object of any class into the CatchAll
smoquet
  • 321
  • 3
  • 11