[英]Django ForeignKey field ignored by graphene-django when "to" value is a string
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.从 3.0 版本升级到 Django 3.2.2 后,观察到以下问题有效。
I have an issue when using graphene-django with a ForeignKey field, it is ignored because the value of to
is a string.我在使用带有 ForeignKey 字段的 graphene-django 时遇到问题,它被忽略了,因为
to
的值是一个字符串。 By ignored, I mean it isn't converted to a GraphQL field.忽略,我的意思是它没有转换为 GraphQL 字段。 Here's the Django model:
这是 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. to
的值是避免循环导入的字符串。 This is also the only field (apart from pk
) on this model.这也是此 model 上的唯一字段(除了
pk
)。
I've created a DjangoObjectType from this class:我从这个 class 创建了一个 DjangoObjectType:
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.我已经将其追踪到长度为 0 的
field_map
。我还观察到上述电缆字段的转换器被调用但返回 None。
This is because field.related_model
returns the string dcim.Cable
but the registry can only lookup by class.这是因为
field.related_model
返回字符串dcim.Cable
但注册表只能通过 class 查找。 So ultimately, _type
is None
below:所以最终,
_type
在下面是None
:
@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
.看来我可以通过覆盖转换器并使用
django.apps
加载 model 来解决此问题。 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.所以我想知道这是否是一个有效的错误和修复(我应该为此提出 PR)或者我的结果出了什么问题。
@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.当您查看调试器时,您会看到
field.related_model
已经解析: Django 这样做,石墨烯甚至看不到字符串引用。
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.但是,如果您没有通过为其创建 DjangoObjectType 并将其导入到架构中将 Cable model 导入注册表,那么 graphene_django 将找不到它。
To make it clear and assuming the app with the CableTermination model is called "main" and your settings file is in project/
:为了清楚起见并假设带有 CableTermination model 的应用程序被称为“main”并且您的设置文件位于
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.
当然,如果您为 Cable model 创建查询和/或突变,这将自行解决,因为您需要为其创建一个类型。
Can't reproduce what you're describing with Django 3.2.3.无法使用 Django 3.2.3 重现您所描述的内容。 Above solution still works for me.
上述解决方案仍然适用于我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.