Django & Graphene:如何处理与多态模型的双向关系?
Django & Graphene: How to handle bidirectional relationship with polmorphic models?
我有一个看起来像这样的 Django 模型(当然是经过简化的):
from django.db import models
from polymorphic.models import PolymorphicModel
class Tournament(models.Model):
slug = models.CharField(max_length=100, unique=True)
class Event(PolymorphicModel):
tournament = models.ForeignKey(Tournament, related_name='events')
slug = models.CharField(max_length=100)
class PracticeEvent(Event):
pass
class MatchEvent(Event):
winner = models.CharField(max_length=100, null=True, blank=True, default=None)
锦标赛由两种项目组成:练习赛和比赛。我想使用 GraphQL,使用 Graphene 公开这个模型。这是我想出的:
import graphene
from graphene_django import DjangoObjectType
from . import models
class TournamentType(DjangoObjectType):
class Meta:
model = models.Tournament
exclude_fields = ('id',)
class EventType(graphene.Interface):
tournament = graphene.Field(TournamentType, required=True)
slug = graphene.String(required=True)
class PracticeEventType(DjangoObjectType):
class Meta:
model = models.PracticeEvent
interfaces = (EventType,)
exclude_fields = ('id',)
class MatchEventType(DjangoObjectType):
class Meta:
model = models.MatchEvent
interfaces = (EventType,)
exclude_fields = ('id',)
extra_types = {PracticeEventType, MatchEventType}
class Query(graphene.ObjectType):
tournaments = graphene.List(TournamentType)
events = graphene.List(EventType)
# ... resolvers ...
schema = graphene.Schema(
query=Query,
types=schema_joust.extra_types,)
到目前为止,还不错;我可以直接查询events { ... }
,连tournament
都可以。但是,由于没有 DjangoObjectType
和 model = models.Event
,我无法查询 tournaments { events {...} }
...
我该如何解决这个问题?我无法将 EventType
设为 DjangoObjectTpe
,而且我不知道在事后添加 events
字段。
也许 Union
类型是您想要的,结合显式声明 EventType
从接口继承:
import graphene
# Rename your existing EventType to EventTypeInterface and redefine like
class EventType(DjangoObjectType):
class Meta:
model = Event
interfaces = [EventTypeInterface]
class EventUnionType(graphene.Union):
@classmethod
def resolve_type(cls, instance, info):
if isinstance(instance, MatchEvent):
return MatchEventType
elif isinstance(instance, PracticeEvent):
return PracticeEventType
return EventType
class Meta:
types = [MatchEventType, PracticeEventType, EventType]
就其本身而言,EventType.tournament
和 TournamentType.events
并不难。第一个如题中所示,第二个可以这样实现:
class EventType(graphene.Interface):
slug = graphene.String(required=True)
class TournamentType(DjangoObjectType):
class Meta:
model = models.Tournament
exclude_fields = ('id',)
events = graphene.List(EventType)
def resolve_events(self, info):
return self.events.all()
graphene-django 无法识别这种关系,但手动声明和解析该字段可以解决问题。为了同时获得 reverse-field,如果我们不需要引用 TournamentType
,它就可以工作,我深入研究了 graphene-django 并找到了 graphene_django.converter.convert_django_field_with_choices
。这让我们可以这样定义字段:
import graphene
from graphene_django import DjangoObjectType, converter, registry
from . import models
class EventType(graphene.Interface):
tournament = converter.convert_django_field_with_choices(
models.Event.tournament.field, registry.get_global_registry())
slug = graphene.String(required=True)
我有一个看起来像这样的 Django 模型(当然是经过简化的):
from django.db import models
from polymorphic.models import PolymorphicModel
class Tournament(models.Model):
slug = models.CharField(max_length=100, unique=True)
class Event(PolymorphicModel):
tournament = models.ForeignKey(Tournament, related_name='events')
slug = models.CharField(max_length=100)
class PracticeEvent(Event):
pass
class MatchEvent(Event):
winner = models.CharField(max_length=100, null=True, blank=True, default=None)
锦标赛由两种项目组成:练习赛和比赛。我想使用 GraphQL,使用 Graphene 公开这个模型。这是我想出的:
import graphene
from graphene_django import DjangoObjectType
from . import models
class TournamentType(DjangoObjectType):
class Meta:
model = models.Tournament
exclude_fields = ('id',)
class EventType(graphene.Interface):
tournament = graphene.Field(TournamentType, required=True)
slug = graphene.String(required=True)
class PracticeEventType(DjangoObjectType):
class Meta:
model = models.PracticeEvent
interfaces = (EventType,)
exclude_fields = ('id',)
class MatchEventType(DjangoObjectType):
class Meta:
model = models.MatchEvent
interfaces = (EventType,)
exclude_fields = ('id',)
extra_types = {PracticeEventType, MatchEventType}
class Query(graphene.ObjectType):
tournaments = graphene.List(TournamentType)
events = graphene.List(EventType)
# ... resolvers ...
schema = graphene.Schema(
query=Query,
types=schema_joust.extra_types,)
到目前为止,还不错;我可以直接查询events { ... }
,连tournament
都可以。但是,由于没有 DjangoObjectType
和 model = models.Event
,我无法查询 tournaments { events {...} }
...
我该如何解决这个问题?我无法将 EventType
设为 DjangoObjectTpe
,而且我不知道在事后添加 events
字段。
也许 Union
类型是您想要的,结合显式声明 EventType
从接口继承:
import graphene
# Rename your existing EventType to EventTypeInterface and redefine like
class EventType(DjangoObjectType):
class Meta:
model = Event
interfaces = [EventTypeInterface]
class EventUnionType(graphene.Union):
@classmethod
def resolve_type(cls, instance, info):
if isinstance(instance, MatchEvent):
return MatchEventType
elif isinstance(instance, PracticeEvent):
return PracticeEventType
return EventType
class Meta:
types = [MatchEventType, PracticeEventType, EventType]
就其本身而言,EventType.tournament
和 TournamentType.events
并不难。第一个如题中所示,第二个可以这样实现:
class EventType(graphene.Interface):
slug = graphene.String(required=True)
class TournamentType(DjangoObjectType):
class Meta:
model = models.Tournament
exclude_fields = ('id',)
events = graphene.List(EventType)
def resolve_events(self, info):
return self.events.all()
graphene-django 无法识别这种关系,但手动声明和解析该字段可以解决问题。为了同时获得 reverse-field,如果我们不需要引用 TournamentType
,它就可以工作,我深入研究了 graphene-django 并找到了 graphene_django.converter.convert_django_field_with_choices
。这让我们可以这样定义字段:
import graphene
from graphene_django import DjangoObjectType, converter, registry
from . import models
class EventType(graphene.Interface):
tournament = converter.convert_django_field_with_choices(
models.Event.tournament.field, registry.get_global_registry())
slug = graphene.String(required=True)