mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-10 19:57:15 +03:00
Add convert_choices_to_enum
option on DjangoObjectType Meta class (#674)
* Add convert_choices_to_enum meta option * Add tests * Run black * Update documentation * Add link to Django choices documentation * Add test and documentation note That setting to an empty list is the same as setting the value as False * Fix Django warning in tests * rst is not markdown
This commit is contained in:
parent
894b1053a2
commit
612ba5a4ea
|
@ -92,6 +92,71 @@ You can completely overwrite a field, or add new fields, to a ``DjangoObjectType
|
|||
return 'hello!'
|
||||
|
||||
|
||||
Choices to Enum conversion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default Graphene-Django will convert any Django fields that have `choices`_
|
||||
defined into a GraphQL enum type.
|
||||
|
||||
.. _choices: https://docs.djangoproject.com/en/2.2/ref/models/fields/#choices
|
||||
|
||||
For example the following ``Model`` and ``DjangoObjectType``:
|
||||
|
||||
.. code:: python
|
||||
|
||||
class PetModel(models.Model):
|
||||
kind = models.CharField(max_length=100, choices=(('cat', 'Cat'), ('dog', 'Dog')))
|
||||
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
|
||||
Results in the following GraphQL schema definition:
|
||||
|
||||
.. code::
|
||||
|
||||
type Pet {
|
||||
id: ID!
|
||||
kind: PetModelKind!
|
||||
}
|
||||
|
||||
enum PetModelKind {
|
||||
CAT
|
||||
DOG
|
||||
}
|
||||
|
||||
You can disable this automatic conversion by setting
|
||||
``convert_choices_to_enum`` attribute to ``False`` on the ``DjangoObjectType``
|
||||
``Meta`` class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
convert_choices_to_enum = False
|
||||
|
||||
.. code::
|
||||
|
||||
type Pet {
|
||||
id: ID!
|
||||
kind: String!
|
||||
}
|
||||
|
||||
You can also set ``convert_choices_to_enum`` to a list of fields that should be
|
||||
automatically converted into enums:
|
||||
|
||||
.. code:: python
|
||||
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
convert_choices_to_enum = ['kind']
|
||||
|
||||
**Note:** Setting ``convert_choices_to_enum = []`` is the same as setting it to
|
||||
``False``.
|
||||
|
||||
|
||||
Related models
|
||||
--------------
|
||||
|
||||
|
|
|
@ -52,13 +52,15 @@ def get_choices(choices):
|
|||
yield name, value, description
|
||||
|
||||
|
||||
def convert_django_field_with_choices(field, registry=None):
|
||||
def convert_django_field_with_choices(
|
||||
field, registry=None, convert_choices_to_enum=True
|
||||
):
|
||||
if registry is not None:
|
||||
converted = registry.get_converted_field(field)
|
||||
if converted:
|
||||
return converted
|
||||
choices = getattr(field, "choices", None)
|
||||
if choices:
|
||||
if choices and convert_choices_to_enum:
|
||||
meta = field.model._meta
|
||||
name = to_camel_case("{}_{}".format(meta.object_name, field.name))
|
||||
choices = list(get_choices(choices))
|
||||
|
|
|
@ -196,6 +196,23 @@ def test_field_with_choices_collision():
|
|||
convert_django_field_with_choices(field)
|
||||
|
||||
|
||||
def test_field_with_choices_convert_enum_false():
|
||||
field = models.CharField(
|
||||
help_text="Language", choices=(("es", "Spanish"), ("en", "English"))
|
||||
)
|
||||
|
||||
class TranslatedModel(models.Model):
|
||||
language = field
|
||||
|
||||
class Meta:
|
||||
app_label = "test"
|
||||
|
||||
graphene_type = convert_django_field_with_choices(
|
||||
field, convert_choices_to_enum=False
|
||||
)
|
||||
assert isinstance(graphene_type, graphene.String)
|
||||
|
||||
|
||||
def test_should_float_convert_float():
|
||||
assert_conversion(models.FloatField, graphene.Float)
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
from collections import OrderedDict, defaultdict
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
from django.db import models
|
||||
from mock import patch
|
||||
|
||||
from graphene import Interface, ObjectType, Schema, Connection, String
|
||||
from graphene import Connection, Field, Interface, ObjectType, Schema, String
|
||||
from graphene.relay import Node
|
||||
|
||||
from .. import registry
|
||||
|
@ -224,3 +229,111 @@ def test_django_objecttype_exclude_fields():
|
|||
|
||||
fields = list(Reporter._meta.fields.keys())
|
||||
assert "email" not in fields
|
||||
|
||||
|
||||
class TestDjangoObjectType:
|
||||
@pytest.fixture
|
||||
def PetModel(self):
|
||||
class PetModel(models.Model):
|
||||
kind = models.CharField(choices=(("cat", "Cat"), ("dog", "Dog")))
|
||||
cuteness = models.IntegerField(
|
||||
choices=((1, "Kind of cute"), (2, "Pretty cute"), (3, "OMG SO CUTE!!!"))
|
||||
)
|
||||
|
||||
yield PetModel
|
||||
|
||||
# Clear Django model cache so we don't get warnings when creating the
|
||||
# model multiple times
|
||||
PetModel._meta.apps.all_models = defaultdict(OrderedDict)
|
||||
|
||||
def test_django_objecttype_convert_choices_enum_false(self, PetModel):
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
convert_choices_to_enum = False
|
||||
|
||||
class Query(ObjectType):
|
||||
pet = Field(Pet)
|
||||
|
||||
schema = Schema(query=Query)
|
||||
|
||||
assert str(schema) == dedent(
|
||||
"""\
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
|
||||
type Pet {
|
||||
id: ID!
|
||||
kind: String!
|
||||
cuteness: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
pet: Pet
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def test_django_objecttype_convert_choices_enum_list(self, PetModel):
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
convert_choices_to_enum = ["kind"]
|
||||
|
||||
class Query(ObjectType):
|
||||
pet = Field(Pet)
|
||||
|
||||
schema = Schema(query=Query)
|
||||
|
||||
assert str(schema) == dedent(
|
||||
"""\
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
|
||||
type Pet {
|
||||
id: ID!
|
||||
kind: PetModelKind!
|
||||
cuteness: Int!
|
||||
}
|
||||
|
||||
enum PetModelKind {
|
||||
CAT
|
||||
DOG
|
||||
}
|
||||
|
||||
type Query {
|
||||
pet: Pet
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def test_django_objecttype_convert_choices_enum_empty_list(self, PetModel):
|
||||
class Pet(DjangoObjectType):
|
||||
class Meta:
|
||||
model = PetModel
|
||||
convert_choices_to_enum = []
|
||||
|
||||
class Query(ObjectType):
|
||||
pet = Field(Pet)
|
||||
|
||||
schema = Schema(query=Query)
|
||||
|
||||
assert str(schema) == dedent(
|
||||
"""\
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
|
||||
type Pet {
|
||||
id: ID!
|
||||
kind: String!
|
||||
cuteness: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
pet: Pet
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -18,7 +18,9 @@ if six.PY3:
|
|||
from typing import Type
|
||||
|
||||
|
||||
def construct_fields(model, registry, only_fields, exclude_fields):
|
||||
def construct_fields(
|
||||
model, registry, only_fields, exclude_fields, convert_choices_to_enum
|
||||
):
|
||||
_model_fields = get_model_fields(model)
|
||||
|
||||
fields = OrderedDict()
|
||||
|
@ -33,7 +35,18 @@ def construct_fields(model, registry, only_fields, exclude_fields):
|
|||
# in there. Or when we exclude this field in exclude_fields.
|
||||
# Or when there is no back reference.
|
||||
continue
|
||||
converted = convert_django_field_with_choices(field, registry)
|
||||
|
||||
_convert_choices_to_enum = convert_choices_to_enum
|
||||
if not isinstance(_convert_choices_to_enum, bool):
|
||||
# then `convert_choices_to_enum` is a list of field names to convert
|
||||
if name in _convert_choices_to_enum:
|
||||
_convert_choices_to_enum = True
|
||||
else:
|
||||
_convert_choices_to_enum = False
|
||||
|
||||
converted = convert_django_field_with_choices(
|
||||
field, registry, convert_choices_to_enum=_convert_choices_to_enum
|
||||
)
|
||||
fields[name] = converted
|
||||
|
||||
return fields
|
||||
|
@ -63,6 +76,7 @@ class DjangoObjectType(ObjectType):
|
|||
connection_class=None,
|
||||
use_connection=None,
|
||||
interfaces=(),
|
||||
convert_choices_to_enum=True,
|
||||
_meta=None,
|
||||
**options
|
||||
):
|
||||
|
@ -90,7 +104,10 @@ class DjangoObjectType(ObjectType):
|
|||
)
|
||||
|
||||
django_fields = yank_fields_from_attrs(
|
||||
construct_fields(model, registry, only_fields, exclude_fields), _as=Field
|
||||
construct_fields(
|
||||
model, registry, only_fields, exclude_fields, convert_choices_to_enum
|
||||
),
|
||||
_as=Field,
|
||||
)
|
||||
|
||||
if use_connection is None and interfaces:
|
||||
|
|
Loading…
Reference in New Issue
Block a user