繁体   English   中英

当“to”值为字符串时,石墨烯-django 忽略 Django ForeignKey 字段

[英]Django ForeignKey field ignored by graphene-django when "to" value is a string

版本:

Python: 3.8
Django: 3.2.2
graphene-django: 2.15.0

从 3.0 版本升级到 Django 3.2.2 后,观察到以下问题有效。

我在使用带有 ForeignKey 字段的 graphene-django 时遇到问题,它被忽略了,因为to的值是一个字符串。 忽略,我的意思是它没有转换为 GraphQL 字段。 这是 Django model:

class CableTermination(models.Model):
    cable = models.ForeignKey(
        to='dcim.Cable',
        on_delete=models.SET_NULL,
        related_name='+',
        blank=True,
        null=True
    )

to的值是避免循环导入的字符串。 这也是此 model 上的唯一字段(除了pk )。

我从这个 class 创建了一个 DjangoObjectType:

class CableTerminationNodeType(DjangoObjectType):
    class Meta:
        model = CableTermination

我也有一种电缆类型:

class CableNodeType(DjangoObjectType):
    class Meta:
        model = Cable

但是在启动时我看到这个错误:

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.

我已经将其追踪到长度为 0 的field_map 。我还观察到上述电缆字段的转换器被调用但返回 None。

这是因为field.related_model返回字符串dcim.Cable但注册表只能通过 class 查找。 所以最终, _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)

有人遇到过类似的问题吗? 或者有什么我应该做的不同的事情吗?

看来我可以通过覆盖转换器并使用django.apps加载 model 来解决此问题。 所以我想知道这是否是一个有效的错误和修复(我应该为此提出 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)

似乎这是问题所在,但事实并非如此。

问题是您没有注册电缆类型。 当您查看调试器时,您会看到field.related_model已经解析: Django 这样做,石墨烯甚至看不到字符串引用。

但是,如果您没有通过为其创建 DjangoObjectType 并将其导入到架构中将 Cable model 导入注册表,那么 graphene_django 将找不到它。

为了清楚起见并假设带有 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'
}

这是最小的设置。 当然,如果您为 Cable model 创建查询和/或突变,这将自行解决,因为您需要为其创建一个类型。

更新:

无法使用 Django 3.2.3 重现您所描述的内容。 上述解决方案仍然适用于我。

调试器显示已解决的 field.related_model

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM