使用嵌套结构的参数使用 django-graphene 过滤父级
Posted
技术标签:
【中文标题】使用嵌套结构的参数使用 django-graphene 过滤父级【英文标题】:Using arguments to nested structures for filtering parents using django-graphene 【发布时间】:2021-10-13 01:10:53 【问题描述】:我目前正在使用带有 Graphene 的 Python 和 Django 来为我的 Graphql 后端建模。我的问题类似于 Graphene 的 repo https://github.com/graphql-python/graphene/issues/431 上的这个问题。我也在使用 graphene_django_optimizer 库来提高性能。
但是,我很难理解如何在当前情况下将@syrusakbary 提出的解决方案应用于我的问题。任何帮助将不胜感激
这是我要执行的查询
getUser(userId: $userId)
id
username
trainings
id
name
sessions
id
createdAt
completedAt
category
培训是正确的,只有属于该特定用户 ID 的培训。然而,每次培训的所有课程都是为所有用户带来的。我希望会话也特定于该单个用户。在我的 types.py 上是相关类型
class SessionType(DjangoObjectType):
class Meta:
model = Session
fields = "__all__"
convert_choices_to_enum = False
@classmethod
def get_queryset(cls, queryset, info, **kwargs):
if info.context.user.is_anonymous:
return queryset.order_by('-id')
return queryset
class TrainingType(gql_optimizer.OptimizedDjangoObjectType):
class Meta:
model = Training
fields = "__all__"
convert_choices_to_enum = False
class UserType(DjangoObjectType):
class Meta:
model = get_user_model()
fields = "__all__"
这是我的相关模型:
class Training(models.Model):
name = models.CharField(max_length=200, help_text='Training\'s name')
details = models.TextField(default="", help_text='Descriptive details about the training')
course = models.ForeignKey("Course", related_name="trainings", on_delete=models.CASCADE)
user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="trainings")
metric_cards = models.TextField(default="", help_text="An object describing the metrics to be used. (Optional)")
def __str__(self):
return str(self.id) + ' - ' + self.name
class Session(models.Model):
name = models.CharField(max_length=200, help_text='Session\'s name')
category = models.CharField(max_length=240, choices=SESSION_CATEGORIES, default="practice",
help_text='Session type. Can be of \'assessment\''
'or \'practice\'')
total_steps = models.IntegerField(default=1, help_text='Amount of steps for this session')
created_at = models.DateTimeField(editable=False, default=timezone.now, help_text='Time the session was created'
'(Optional - default=now)')
completed_at = models.DateTimeField(editable=False, null=True, blank=True, help_text='Time the session was finished'
'(Optional - default=null)')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="training_sessions", on_delete=models.DO_NOTHING)
training = models.ForeignKey("Training", related_name="sessions", on_delete=models.CASCADE)
def __str__(self):
return self.name
根据每种类型,我的解析器位于单独的文件中,例如 类 SessionQuery(graphene.ObjectType) 包含与会话相关的所有解析器。像这样的:
class SessionQuery(graphene.ObjectType):
debug = graphene.Field(DjangoDebug, name='_debug')
# Session Queries
session = graphene.Field(SessionType, id=graphene.Int(), user_id=graphene.Int(required=True))
last_created_session = graphene.Field(SessionType)
all_sessions = graphene.List(SessionType,
first=graphene.Int(),
skip=graphene.Int(),)
latest_activities = graphene.List(LatestActivity, limit=graphene.Int())
activity_in_range = graphene.List(UserRangeActivity, start_date=graphene.Date(), end_date=graphene.Date())
unique_users_activity_in_range = graphene.Int(start_date=graphene.Date(), end_date=graphene.Date())
def resolve_last_created_session(root, info):
return Session.objects.latest('id')
def resolve_all_sessions(root, info,first=None,skip=None):
if skip:
return gql_optimizer.query(Session.objects.all().order_by('-id')[skip:], info)
elif first:
return gql_optimizer.query(Session.objects.all().order_by('-id')[:first], info)
else:
return gql_optimizer.query(Session.objects.all().order_by('-id'), info)
(只是代码的一部分,因为太多了,其余的无关紧要)
现在,据我所知,要实现我想要的,我需要在 TrainingQuery 类下有一个类似 resolve_sessions 的东西,在那里我可以有一个参数 user_id,我只是从嵌套链中传递下来的。但我没有该领域的解析器。 Sessions 是一个 Session 的列表,它是 Training 模型中的外键,当我在查询中有这样的东西时会自动带上这个列表:
training
id
name
sessions
id
name
我想我想要实现的查询是这样的:
query getUser($userId: Int!)
getUser(userId: $userId)
id
username
trainings
id
name
sessions(userId: Int)
id
createdAt
completedAt
category
但是我可以在哪个地方/解析器实现这样的事情?它在我的 SessionType 的 get_queryset 方法中吗?如果有,怎么做?
我在正确的道路上吗?
【问题讨论】:
【参考方案1】:我找到了解决方案。是的,我走在正确的轨道上。问题实际上是关于石墨烯的糟糕文档。我不得不打开 ResolveInfo 对象的源代码。可以看here
基本上,在查询的父级传递的参数在 info.variable_values 下可用。所以我需要做的就是修改 get_queryset 方法并像这样:
class SessionType(DjangoObjectType):
class Meta:
model = Session
fields = "__all__"
convert_choices_to_enum = False
@classmethod
def get_queryset(cls, queryset, info, **kwargs):
if info.variable_values.get('userId') and info.variable_values.get('userId') is not None:
return queryset.filter(Q(user_id=info.variable_values.get('userId')))
return queryset
这是一件非常重要的事情,我们通常希望过滤器以这种方式工作。我希望他们将这个“技巧”添加到他们的文档中。希望这个答案能帮助遇到同样问题的其他人
【讨论】:
【参考方案2】:听起来您想修改TrainingType
上的sessions
字段以添加新参数,并向类添加resolve_sessions
方法。
根据我在您的问题中看到的情况,应该是这样的:
class TrainingType(graphene.ObjectType):
# other fields omitted
sessions = graphene.List(
SessionType,
user_id=graphene.ID(),
first=graphene.Int(),
skip=graphene.Int(),
)
def resolve_sessions(self, info, user_id=None, first=None, skip=None):
# Change this to do something other than return an empty list
return []
【讨论】:
不,不幸的是,这不起作用。因为会话是由 fk 关系自动生成的,所以添加这个解析不会覆盖任何会话应该带来的。但好消息是我找到了方法。我会在这里发布答案:)。无论如何感谢您的帮助以上是关于使用嵌套结构的参数使用 django-graphene 过滤父级的主要内容,如果未能解决你的问题,请参考以下文章