Add BlankField and mount enums using it

This commit is contained in:
Jonathan Kim 2020-07-22 09:36:29 +01:00
parent 8571bc465a
commit fd2dd3adae
2 changed files with 74 additions and 9 deletions

View File

@ -1,11 +1,17 @@
from collections import OrderedDict from collections import OrderedDict
from functools import partial, wraps
from django.db import models from django.db import models
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from graphql import assert_valid_name
from graphene import ( from graphene import (
ID, ID,
UUID,
Boolean, Boolean,
Date,
DateTime,
Dynamic, Dynamic,
Enum, Enum,
Field, Field,
@ -14,24 +20,39 @@ from graphene import (
List, List,
NonNull, NonNull,
String, String,
UUID,
DateTime,
Date,
Time, Time,
) )
from graphene.types.resolver import get_default_resolver
from graphene.types.json import JSONString from graphene.types.json import JSONString
from graphene.utils.str_converters import to_camel_case from graphene.utils.str_converters import to_camel_case
from graphql import assert_valid_name
from .settings import graphene_settings
from .compat import ArrayField, HStoreField, JSONField, PGJSONField, RangeField from .compat import ArrayField, HStoreField, JSONField, PGJSONField, RangeField
from .fields import DjangoListField, DjangoConnectionField from .fields import DjangoConnectionField, DjangoListField
from .settings import graphene_settings
from .utils import import_single_dispatch from .utils import import_single_dispatch
from .utils.str_converters import to_const from .utils.str_converters import to_const
singledispatch = import_single_dispatch() singledispatch = import_single_dispatch()
class BlankValueField(Field):
def get_resolver(self, parent_resolver):
resolver = self.resolver or parent_resolver
# create custom resolver
def blank_field_wrapper(func):
@wraps(func)
def wrapped_resolver(*args, **kwargs):
return_value = func(*args, **kwargs)
if return_value == "":
return None
return return_value
return wrapped_resolver
return blank_field_wrapper(resolver)
def convert_choice_name(name): def convert_choice_name(name):
name = to_const(force_str(name)) name = to_const(force_str(name))
try: try:
@ -68,7 +89,8 @@ def convert_choices_to_named_enum_with_descriptions(name, choices):
def description(self): def description(self):
return named_choices_descriptions[self.name] return named_choices_descriptions[self.name]
return Enum(name, list(named_choices), type=EnumWithDescriptionsType) return_type = Enum(name, list(named_choices), type=EnumWithDescriptionsType)
return return_type
def generate_enum_name(django_model_meta, field): def generate_enum_name(django_model_meta, field):
@ -105,9 +127,12 @@ def convert_django_field_with_choices(
return converted return converted
choices = getattr(field, "choices", None) choices = getattr(field, "choices", None)
if choices and convert_choices_to_enum: if choices and convert_choices_to_enum:
enum = convert_choice_field_to_enum(field) EnumCls = convert_choice_field_to_enum(field)
required = not (field.blank or field.null) required = not (field.blank or field.null)
converted = enum(description=field.help_text, required=required)
converted = EnumCls(description=field.help_text, required=required).mount_as(
BlankValueField
)
else: else:
converted = convert_django_field(field, registry) converted = convert_django_field(field, registry)
if registry is not None: if registry is not None:

View File

@ -394,3 +394,43 @@ def test_generate_enum_name(graphene_settings):
generate_enum_name(model_meta, field) generate_enum_name(model_meta, field)
== "SomeLongAppNameSomeObjectFizzBuzzChoices" == "SomeLongAppNameSomeObjectFizzBuzzChoices"
) )
def test_choice_enum_blank_value():
"""Test that choice fields with blank values work"""
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
fields = (
"first_name",
"a_choice",
)
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
def resolve_reporter(root, info):
return Reporter.objects.first()
schema = graphene.Schema(query=Query)
# Create model with empty choice option
Reporter.objects.create(
first_name="Bridget", last_name="Jones", email="bridget@example.com"
)
result = schema.execute(
"""
query {
reporter {
firstName
aChoice
}
}
"""
)
assert not result.errors
assert result.data == {
"reporter": {"firstName": "Bridget", "aChoice": None},
}