Search code examples
postgresqlgraphene-pythongraphene-django

Return Django ORM union result in Django-Graphene


I am trying to query two separate objects and return them as a single result set. I've tried using a Union and an Interface, but not sure what I'm doing wrong there.

My model:

class BaseActivity(models.Model):
    class Meta:
        abstract = True

    name = models.CharField(db_column="activity_type_name", unique=True, max_length=250)
    created_at = CreationDateTimeField()
    modified_at = ModificationDateTimeField()
    created_by = models.UUIDField()
    modified_by = models.UUIDField()
    deleted_at = models.DateTimeField(blank=True, null=True)
    deleted_by = models.UUIDField(blank=True, null=True)


class Activity(BaseActivity):
    class Meta:
        db_table = "activity_type"
        ordering = ("sort_order",)

    id = models.UUIDField(
        db_column="activity_type_id",
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
    )
    sort_order = models.IntegerField()

    def __str__(self):
        return self.name


class CustomActivity(BaseActivity):
    class Meta:
        db_table = "organization_custom_activity_type"

    id = models.UUIDField(
        db_column="organization_custom_activity_type",
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
    )
    farm = models.ForeignKey("farm.Farm", db_column="organization_id", on_delete=models.DO_NOTHING, related_name="custom_activity_farm")

My schema:

class FarmActivities(graphene.ObjectType):
    id = graphene.String()
    name = graphene.String()


class ActivityType(DjangoObjectType):
    class Meta:
        model = Activity
        fields = ("id", "name", "requires_crop", "sort_order")



class CustomActivityType(DjangoObjectType):
    class Meta:
        model = CustomActivity
    fields = ("id", "name", "farm")

And the query:

class Query(graphene.ObjectType):
    get_farm_activities = graphene.Field(FarmActivities, farm=graphene.String(required=True))

    def resolve_get_farm_activities(self, info, farm):

        farm_activities = Activity.objects.values("id", "name").filter(
            farmtypeactivityrel__farm_type__farm=farm, deleted_at=None
        )

        custom_activities = CustomActivity.objects.values("id", "name").filter(farm=farm, deleted_at=None)

        return list(chain(farm_activities, custom_activities))

With this, I do get a list back from the query, but it's not going thru the resolver when I call getFarmActivities.

Literally the list returns:

ExecutionResult(data={'getFarmActivities': {'id': None, 'name': None}}, errors=None)

Solution

  • How to resolve graphene.Union Type?

    That provided the hint I needed to get this working. I had to build a Union that would parse the model and not the schema type.

    class FarmActivities(graphene.Union):
        class Meta:
            types = (ActivityType, CustomActivityType)
    
        @classmethod
        def resolve_type(cls, instance, info):
            if isinstance(instance, Activity):
                return ActivityType
            if isinstance(instance, CustomActivity):
                return CustomActivityType
    
            return FarmActivities.resolve_type(instance, info)
    

    Which then allowed me to run the query like so:

    def resolve_get_farm_activities(self, info, farm):

    farm_activities = Activity.objects.filter(
        farmtypeactivityrel__farm_type__farm=farm
    )
    
    custom_activities = CustomActivity.objects.filter(farm=farm)
    
    return list(chain(farm_activities, custom_activities))
    

    And the API query:

        query farmParam($farm: String!)
        {
            getFarmActivities(farm: $farm)
            {
                ... on CustomActivityType
                {
                    id
                    name
                }
                ... on ActivityType
                {
                    id
                    name
                }
            }
        }