thinwybk
thinwybk

Reputation: 4743

How can I use a single enum for django model and graphql mutation arguments?

I've defined Django models with fields containing text choices corresponding to enums. The GraphQL API provides mutations (which are not derived from models directly) with arguments of type enum which shall accept the same values as the models only. How can I get rid of my dublication?

models.py:

class SomeModel(models.Model):

    class SomeEnum(models.TextChoices):

        A = "A", _("Option A")
        B = "B", _("Option B")

    enum_field = models.CharField(
        max_length=1,
        choices=SomeEnum.choices,
        default=SomeEnum.A,
    )

schema.py:


class SomeEnumSchema(graphene.Enum):

    A = "A"
    B = "B"


class SomeMutation(graphene.Mutation):

    class Arguments:
        some_enum = SomeEnumSchema(required=True)

Upvotes: 6

Views: 4253

Answers (3)

Enogwe Victor
Enogwe Victor

Reputation: 80

extend your graphene.Enum instances with the class below

from inspect import getmembers, isroutine

class EnumChoices:
    @classmethod
    def choices(self):
        attributes = getmembers(self, lambda a: not (isroutine(a)))
        values = [(a[0], a[1]._value_) for a in attributes if hasattr(a[1], "_value_")]
        return values

Your Example Enum will look like the one below

# import the EnumChoices class above 
from graphene import Enum

class YourExampleChoices(EnumChoices, Enum):
    BAD = "Bad"
    MEH = "Meh"
    NORMAL = "Normal"
    LIKE = "Like"
    LOVE = "Love"

Your example graphene.types.InputObjectType will look like the one below

# import the YourExampleChoices class above
from graphene.types import InputObjectType

class YourExampleChoicesMutationInput(InputObjectType):
    choice = YourExampleChoices(required=True)

Your example graphene.Mutation will look like the one below

# import the YourExampleChoicesMutationInput class above 
import graphene

from graphene.types.mutation import Mutation

class YourExampleMutation(Mutation):
    # make sure to define your mutation results fields

    class Arguments:
        input = YourExampleChoicesMutationInput(required=True)

    def mutate(root, info, input):
        # what do you want this mutation to do?
        pass

Your Django model will look like the one below

# import the YourExampleChoices class above
# notice that `YourExampleChoices.choices()` is callable, this is slightly different from the `Textchoices.choices` which isn't callable

class YourExampleModel(TimestampBase):
    choices = models.CharField(
        max_length=6, 
        choices=YourExampleChoices.choices(), 
        default=YourExampleChoices.NORMAL._value_
    )

Upvotes: 1

Lukasz Dynowski
Lukasz Dynowski

Reputation: 13640

If you'd like to use Enums in custom mutations then, you could use graphine.Enum like this.

class PermisionEnum(graphene.Enum):
    READ = "read"
    WRITE = "write"


class SimpleModelMutation(graphene.Mutation):

    class Arguments:
        # reusable
        permission = PermisionEnum(required=True)
        # not reusable
        scope = graphene.Enum('scope', [
            ("PERSONAL", "personal"),
            ("PROJECT", "project")
        ])(required=True)

I recommend to use PermisionEnum(required=True) approach because with it PermisionEnum class can be reuse across different mutations. This is not possible when you declare enums as I do it with scope filed!

Upvotes: 1

Kant Chen
Kant Chen

Reputation: 403

You can use graphene.Enum.from_enum().

This function can convert normal Enum type to graphene.Enum.

Do notice that models.TextChoices is only available for Dajango version above 3.0

models.py (for Django version >= 3.0)

from django.db import models

class SomeModel(models.Model):

    class SomeEnum(models.TextChoices):

        A = "A", _("Option A")
        B = "B", _("Option B")

    enum_field = models.CharField(
        max_length=1,
        choices=SomeEnum.choices,
        default=SomeEnum.A,
    )

models.py (for Django version < 3.0)

from enum import Enum
class SomeEnum(Enum):
    A = "A"
    B = "B"

schema.py:

SomeEnumSchema = graphene.Enum.from_enum(SomeEnum)
class SomeMutation(graphene.Mutation):
    class Arguments:
        some_enum = SomeEnumSchema(required=True)

Upvotes: 13

Related Questions