Reputation: 561
I'm building a simple recipe storage application that uses the Graphene package for GraphQL. I've been able to use Django Forms so far very easily in my mutations, however one of my models fields is really an Enum and I'd like to expose it in Graphene/GraphQL as such.
My enum:
class Unit(Enum):
# Volume
TEASPOON = "teaspoon"
TABLESPOON = "tablespoon"
FLUID_OUNCE = "fl oz"
CUP = "cup"
US_PINT = "us pint"
IMPERIAL_PINT = "imperial pint"
US_QUART = "us quart"
IMPERIAL_QUART = "imperial quart"
US_GALLON = "us gallon"
IMPERIAL_GALLON = "imperial gallon"
MILLILITER = "milliliter"
LITER = "liter"
# Mass and Weight
POUND = "pound"
OUNCE = "ounce"
MILLIGRAM = "milligram"
GRAM = "gram"
KILOGRAM = "kilogram"
My Model:
class RecipeIngredient(TimeStampedModel):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredients')
direction = models.ForeignKey(RecipeDirection, on_delete=models.CASCADE, null=True, related_name='ingredients')
quantity = models.DecimalField(decimal_places=2, max_digits=10)
unit = models.TextField(choices=Unit.as_tuple_list())
My form:
class RecipeIngredientForm(forms.ModelForm):
class Meta:
model = RecipeIngredient
fields = (
'recipe',
'direction',
'quantity',
'unit',
)
My Mutation:
class CreateRecipeIngredientMutation(DjangoModelFormMutation):
class Meta:
form_class = RecipeIngredientForm
exclude_fields = ('id',)
I've created this graphene enum UnitEnum = Enum.from_enum(Unit)
however I haven't been able to get graphene to pick it up. I've tried adding it to the CreateRecipeIngredientMutation
as a regular field like unit = UnitEnum()
as well as an Input class on that mutation. So far, the closest I've gotten is this Github issue from awhile ago. After playing around with the class in an iPython shell, I think I could just do CreateRecipeIngredientMutation.Input.unit.type.of_type = UnitEnum()
but this feels awful.
Upvotes: 3
Views: 1667
Reputation: 561
I came up with a solution that works but is not pretty. I used the https://github.com/hzdg/django-enumfields package to help with this.
I created my own form field:
class EnumChoiceField(enumfields.forms.EnumChoiceField):
def __init__(self, enum, *, coerce=lambda val: val, empty_value='', **kwargs):
if isinstance(enum, six.string_types):
self.enum = import_string(enum)
else:
self.enum = enum
super().__init__(coerce=coerce, empty_value=empty_value, **kwargs)
And used it in my Django form. Then in my custom AppConfig I did this:
class CoreAppConfig(AppConfig):
name = 'myapp.core'
def ready(self):
registry = get_global_registry()
@convert_form_field.register(EnumChoiceField)
def convert_form_field_to_enum(field: EnumChoiceField):
converted = registry.get_converted_field(field.enum)
if converted is None:
raise ImproperlyConfigured("Enum %r is not registered." % field.enum)
return converted(description=field.help_text, required=field.required)
And finally in my schema:
UnitEnum = Enum.from_enum(Unit)
get_global_registry().register_converted_field(Unit, UnitEnum)
I really don't like this, but couldn't think of a better way to handle this. I came across this idea when searching down another graphene django issue here https://github.com/graphql-python/graphene-django/issues/481#issuecomment-412227036.
I feel like there has to be a better way to do this.
Upvotes: 1