Versions:
Python: 3.8
Django: 3.2.2
graphene-django: 2.15.0
The below issue worked was observed after upgrading to Django 3.2.2 from a 3.0 version.
I have an issue when using graphene-django with a ForeignKey field, it is ignored because the value of to
is a string. By ignored, I mean it isn't converted to a GraphQL field. Here's the Django model:
class CableTermination(models.Model):
cable = models.ForeignKey(
to='dcim.Cable',
on_delete=models.SET_NULL,
related_name='+',
blank=True,
null=True
)
The value of to
is a string to avoid a circular import. This is also the only field (apart from pk
) on this model.
I've created a DjangoObjectType from this class:
class CableTerminationNodeType(DjangoObjectType):
class Meta:
model = CableTermination
I also have a type for Cable:
class CableNodeType(DjangoObjectType):
class Meta:
model = Cable
But on startup I see this error:
env/lib/python3.8/site-packages/graphql/type/definition.py", line 214, in define_field_map
assert isinstance(field_map, Mapping) and len(field_map) > 0, (
AssertionError: CableTerminationNodeType fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.
I've tracked this down to field_map
having length 0. I have also observed that the converter for the above cable field is called but returns None.
This is because field.related_model
returns the string dcim.Cable
but the registry can only lookup by class. So ultimately, _type
is None
below:
@convert_django_field.register(models.OneToOneField)
@convert_django_field.register(models.ForeignKey)
def convert_field_to_djangomodel(field, registry=None):
model = field.related_model
def dynamic_type():
_type = registry.get_type_for_model(model)
if not _type:
return
return Field(_type, description=field.help_text, required=not field.null)
return Dynamic(dynamic_type)
Anyone come across a similar issue? Or is there something I should be doing differently?
It appears I can workaround this by overriding the converter and loading the model with django.apps
. So I'm wondering if this is a valid bug and fix (which I should raise a PR for) or something going wrong on my end.
@convert_django_field.register(models.OneToOneField)
@convert_django_field.register(models.ForeignKey)
def convert_field_to_djangomodel(field, registry=None):
model = field.related_model
if isinstance(model, str):
split = model.split('.', 1)
model = apps.get_model(app_label=split[0], model_name=split[1])
def dynamic_type():
_type = registry.get_type_for_model(model)
if not _type:
return
return Field(_type, description=field.help_text, required=not field.null)
return Dynamic(dynamic_type)
It seems this is the problem, but it isn't.
The problem is that you didn't register a Cable type. When you look into the debugger you will see that field.related_model
is already resolved: Django does this, graphene doesn't even see the string reference.
But if you don't get the Cable model into the registry by creating a DjangoObjectType for it and import it into the schema, graphene_django cannot find it.
To make it clear and assuming the app with the CableTermination model is called "main" and your settings file is in project/
:
# file: dcim/schema.py
from graphene_django import DjangoObjectType
from .models import Cable
class CableType(DjangoObjectType):
class Meta:
model = Cable
# main/schema.py
import graphene
from graphene_django import DjangoObjectType
from .models import CableTermination
class CableTerminationType(DjangoObjectType):
class Meta:
model = CableTermination
class Query(graphene.ObjectType):
terminations = graphene.List(CableTerminationType)
@staticmethod
def resolve_terminations(root, info, **kwargs):
return CableTermination.objects.all()
#project/schema.py:
import graphene
import main.schema
# import the schema with the CableType, so it registers itself
import dcim.schema
class Query(main.schema.Query, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query)
# project/settings.py
GRAPHENE = {
"SCHEMA": 'project.schema.schema'
}
This is the minimal setup. Of course, if you create queries and/or mutations for the Cable model, this will resolve itself, since you will need to create a type for it.
Can't reproduce what you're describing with Django 3.2.3. Above solution still works for me.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.