Graphene-django: DjangoObjectType interprets string choices fields as required in schema even with blank=True attribute

Created on 23 Aug 2018  路  8Comments  路  Source: graphql-python/graphene-django

To reproduce this problem, create a Django model with a choices field with blank=True:

class MyModel(models.Model):
    PERIODIC_INTERVAL_CHOICES = (('Weekly', 'Weekly'),
         ('Bi-Weekly', 'Bi-Weekly'),
          ('Monthly', 'Monthly'),
          ('Quarterly', 'Quarterly'),
          ('Semi-Annually', 'Semi-Annually'),
          'Annually', 'Annually'))

    payment_frequency = models.CharField(
        blank=True,
        choices=PERIODIC_INTERVAL_CHOICES,
        max_length=13)

The schema generated by DjangoObjectType will show incorrectly show that this field payment_frequency is required. This is incorrect -- the expected behavior is that it NOT required.

I have been able to fix this issue by patching how the value of required is determined in graphene.converter.convert_django_field_with_choices (shown for graphene-django 2.0.0) :

def convert_django_field_with_choices(field, registry=None):
    # Modified from graphene_django.converter import convert_django_field_with_choices
    # to adjust "required"
    choices = getattr(field, 'choices', None)
    if choices:
        meta = field.model._meta
        name = to_camel_case('{}_{}'.format(meta.object_name, field.name))
        choices = list(get_choices(choices))
        named_choices = [(c[0], c[1]) for c in choices]
        named_choices_descriptions = {c[0]: c[2] for c in choices}

        class EnumWithDescriptionsType(object):

            @property
            def description(self):
                return named_choices_descriptions[self.name]

        enum = Enum(name, list(named_choices), type=EnumWithDescriptionsType)
        required = not (field.blank or field.null or field.default)  # MODIFIED FROM ORIGINAL
        return enum(description=field.help_text, required=required)
    return convert_django_field(field, registry)

Most helpful comment

Just want to add a bump to this thread after spending some time dealing with this issue. Django specifies that empty CharFields should be represented as "" rather than None so that there is only one representation of an empty field ("" rather than "" and None). This means setting blank=True and NOT setting null=True on the CharField model definition (https://docs.djangoproject.com/en/2.2/ref/models/fields/#null). Graphene-django v2.4.0 does not handle this case correctly. CharFields with blank=True show as required and will raise an error: "Expected a value of type \"{field_from_choices_array}\" but received: ", Graphene-django is saying the field is required to be defined and that the "" value is not allowed; however, the "" character is indeed an allowed character for this field in django--in fact it's the idiomatic character for an empty CharField.

While this thread suggests that explicitly defining the blank field is more idiomatic, django already has a default value for blank CharFields, "", and using this seems like a sensible default for Graphene-django rather than requiring the "" field to be explicitly defined a la this thread on every CHOICES array that is set on a CharField(blank=True).

Additionally, if I define a "" choice for my CharField(blank=True) as per the suggestions in the links above, graphene-django returns an "A_" value for the blank field--less than ideal.

I'd suggest that and CharField(blank=True) with a choices set should be correctly identified by graphene in the schema as NOT required and to correctly handle a "" value from django by returning a "" value to the GQL client.

All 8 comments

Hi @picturedots, I have tried your solution but still the error reported

The code above is for iillustrative purposes of where in the code the issue lies. To actually use my code you need to replace every single one of your string enum fields, using a method like

def create_enum_field(django_model, field_name):
    """ 
    Creates a graphene Enum field from a Django choices field that is
    suitable for inputs and outputs.
    """
    django_field = django_model._meta.get_field(field_name)
    graphene_field = convert_django_field_with_choices(django_field)
    return graphene_field

that is called in a DjangoObjectType like

payment_frequency = create_enum_field(MyModel, 'payment_frequency')

Hi @picturedots. Do you have a temporary solution for this while waiting for the error to be fixed?

What I did for now is to add a field in DjangoObjectType with the same name and use the field as source.

class MyModel(DjangoObjectType):
    payment_frequency = graphene.String(source='payment_frequency')

The only problem with this is that the payment_frequency is identified as a String instead of MyModelPaymentFrequency

Yes, @picturedots I tried your suggestion as well (even after replacing the fields in my DjangoObjecttype) and experienced the same errors. I wonder what the difference is between our codebases. Not sure how to solve it.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Hey, bot -- this issue is not fix as far as I know. Please don't close.

Just want to add a bump to this thread after spending some time dealing with this issue. Django specifies that empty CharFields should be represented as "" rather than None so that there is only one representation of an empty field ("" rather than "" and None). This means setting blank=True and NOT setting null=True on the CharField model definition (https://docs.djangoproject.com/en/2.2/ref/models/fields/#null). Graphene-django v2.4.0 does not handle this case correctly. CharFields with blank=True show as required and will raise an error: "Expected a value of type \"{field_from_choices_array}\" but received: ", Graphene-django is saying the field is required to be defined and that the "" value is not allowed; however, the "" character is indeed an allowed character for this field in django--in fact it's the idiomatic character for an empty CharField.

While this thread suggests that explicitly defining the blank field is more idiomatic, django already has a default value for blank CharFields, "", and using this seems like a sensible default for Graphene-django rather than requiring the "" field to be explicitly defined a la this thread on every CHOICES array that is set on a CharField(blank=True).

Additionally, if I define a "" choice for my CharField(blank=True) as per the suggestions in the links above, graphene-django returns an "A_" value for the blank field--less than ideal.

I'd suggest that and CharField(blank=True) with a choices set should be correctly identified by graphene in the schema as NOT required and to correctly handle a "" value from django by returning a "" value to the GQL client.

I am still having this exact issue even after the #714 . Anyone else experiencing the same problem?

Was this page helpful?
0 / 5 - 0 ratings