Django Graphene 编写具有多层嵌套外键的突变
Django Graphene writing mutations with multiple layers of nested foreign keys
你如何编写嵌套外键的模式和查询?我检查了文档,没有发现如何执行此操作的示例。所以这是我基于 github 的尝试,Whosebug 的答案可以说我有这些模型:
class Address(models.Model):
name = models.CharField()
class Person(models.Model):
name = models.CharField()
address = models.ForeignKey('Address', on_delete=models.CASCADE, blank=False, null=False)
class Blog(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=False, null=False)
text = models.TextField()
我尝试编写这样的架构:
class AddressInput(graphene.InputObjectType):
name = graphene.String(required=True)
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
address =graphene.Field(AddressInput)
class CreateNewBlog(graphene.Mutation):
blog=graphene.Field(BlogType)
class Arguments:
address_data = AddressInput()
person_data = PersonInput()
text = graphene.String()
@staticmethod
def mutate(root, info, person_data=None, address_data=None, **input):
address = Address.objects.create(name=address_data.name)
person = Person.objects.create(address=address, name=person_data.name)
blog = Blog.objects.create(person =person, text=input['text'])
blog.save()
return CreateNewBlog(blog=blog)
我使用了这样的查询:
mutation {
CreateNewBlog(person: { address: {name: "aaa"},
name: "First Last" }, text: "hi hi") {
Blog {
person{
name
address{
name
}
},
text
}
}
}
我收到此错误消息:
{
"errors": [
{
"message": "'NoneType' object has no attribute 'name'",
"locations": [
{
"line": 32,
"column": 9
}
],
"path": [
"CreateNewBlog"
]
}
],
"data": {
"CreateNewBlog": null
}
}
我认为问题出在我编写 schema.py 文件的方式上。将 InputFields 嵌套在另一个 InputField 中不起作用的地方。还有其他方法可以写一个单一的突变吗?
好的,这里有几件事。首先,您应该生成 schema.graphql
文件,因为它会向您显示由 Graphene 构建的模式的实际最终形状,这将使您的调试更容易。或者您可以使用 GraphiQL 来测试您的查询并让它的文档和自动完成为您完成繁重的工作。
但具体而言,您的石墨烯突变定义将生成如下所示的突变:
input AddressInput {
name: String!
}
input PersonInput {
name: String!
address: AddressInput
}
type CreateNewBlogOutput {
blog: Blog
}
type Mutation {
CreateNewBlog(addressData: AddressInput, personData: PersonInput, text: String): CreateNewBlogOutput!
}
值得注意的是,您可以通过两种方式在此处提供 AddressInput,一种在根目录中,一种在 PersonInput 中。这可能不是您打算做的。其次,需要 none 的根参数,这导致您的错误消息相当无用,因为问题是您调用的变异参数不正确但查询验证器允许它通过,因为您的类型非常宽容。
我相信如果你要运行像下面这样的突变,它实际上会起作用:
mutation {
CreateNewBlog(
personData: {
address: {
name: "aaa"
},
name: "First Last"
},
text: "hi hi"
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
我这里只做了两处改动,person
改为 personData
(为了匹配你的突变定义,Graphene 自动完成从蛇案例到骆驼案例的对话),Blog
到 blog
在字段选择中。
但让我们更进一步,这是我进行突变的方式。
class AddressInput(graphene.InputObjectType):
name = graphene.String(required=True)
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
address = AddressInput(required=True)
class CreateNewBlogInput(graphene.InputObjectType):
person = PersonInput(required=True)
text = graphene.String(required=True)
class CreateNewBlogPayload(graphene.ObjectType):
blog = graphene.Field(BlogType, required=True)
class CreateNewBlog(graphene.Mutation):
class Arguments:
input_data = CreateNewBlogInput(required=True, name="input")
Output = CreateNewBlogPayload
@staticmethod
def mutate(root, info, input_data):
address = Address.objects.create(name=input_data.person.address.name)
person = Person.objects.create(address=address, name=input_data.person.name)
blog = Blog.objects.create(person=person, text=input_data.text)
blog.save()
return CreateNewBlogPayload(blog=blog)
在构造 Graphene 的变异对象时,我也会将 CreateNewBlog
更改为 createNewBlog
,因为 GraphQL 惯例是使用小驼峰式大小写进行变异。
然后你会 运行 像这样:
mutation {
createNewBlog(
input: {
person: {
address: {
name: "aaa"
},
name: "First Last"
}
text: "hi hi"
}
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
为什么要将整个输入包装在一个输入字段中?主要是因为它使得在使用变量时在客户端更容易调用突变,你可以只提供正确形状的单个输入 arg 而不是多个。
// So instead of this
mutation OldCreateNewBlog($person: PersonInput, $text: String) {
createNewBlog(
personData: $person
text: $text
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
// You have this
mutation NewCreateNewBlog($input: CreateNewBlogInput!) {
createNewBlog(
input: $input
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
后者可以更轻松地随时间更改输入形状,并且只需在客户端代码中的一个地方进行更改。
你如何编写嵌套外键的模式和查询?我检查了文档,没有发现如何执行此操作的示例。所以这是我基于 github 的尝试,Whosebug 的答案可以说我有这些模型:
class Address(models.Model):
name = models.CharField()
class Person(models.Model):
name = models.CharField()
address = models.ForeignKey('Address', on_delete=models.CASCADE, blank=False, null=False)
class Blog(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=False, null=False)
text = models.TextField()
我尝试编写这样的架构:
class AddressInput(graphene.InputObjectType):
name = graphene.String(required=True)
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
address =graphene.Field(AddressInput)
class CreateNewBlog(graphene.Mutation):
blog=graphene.Field(BlogType)
class Arguments:
address_data = AddressInput()
person_data = PersonInput()
text = graphene.String()
@staticmethod
def mutate(root, info, person_data=None, address_data=None, **input):
address = Address.objects.create(name=address_data.name)
person = Person.objects.create(address=address, name=person_data.name)
blog = Blog.objects.create(person =person, text=input['text'])
blog.save()
return CreateNewBlog(blog=blog)
我使用了这样的查询:
mutation {
CreateNewBlog(person: { address: {name: "aaa"},
name: "First Last" }, text: "hi hi") {
Blog {
person{
name
address{
name
}
},
text
}
}
}
我收到此错误消息:
{
"errors": [
{
"message": "'NoneType' object has no attribute 'name'",
"locations": [
{
"line": 32,
"column": 9
}
],
"path": [
"CreateNewBlog"
]
}
],
"data": {
"CreateNewBlog": null
}
}
我认为问题出在我编写 schema.py 文件的方式上。将 InputFields 嵌套在另一个 InputField 中不起作用的地方。还有其他方法可以写一个单一的突变吗?
好的,这里有几件事。首先,您应该生成 schema.graphql
文件,因为它会向您显示由 Graphene 构建的模式的实际最终形状,这将使您的调试更容易。或者您可以使用 GraphiQL 来测试您的查询并让它的文档和自动完成为您完成繁重的工作。
但具体而言,您的石墨烯突变定义将生成如下所示的突变:
input AddressInput {
name: String!
}
input PersonInput {
name: String!
address: AddressInput
}
type CreateNewBlogOutput {
blog: Blog
}
type Mutation {
CreateNewBlog(addressData: AddressInput, personData: PersonInput, text: String): CreateNewBlogOutput!
}
值得注意的是,您可以通过两种方式在此处提供 AddressInput,一种在根目录中,一种在 PersonInput 中。这可能不是您打算做的。其次,需要 none 的根参数,这导致您的错误消息相当无用,因为问题是您调用的变异参数不正确但查询验证器允许它通过,因为您的类型非常宽容。
我相信如果你要运行像下面这样的突变,它实际上会起作用:
mutation {
CreateNewBlog(
personData: {
address: {
name: "aaa"
},
name: "First Last"
},
text: "hi hi"
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
我这里只做了两处改动,person
改为 personData
(为了匹配你的突变定义,Graphene 自动完成从蛇案例到骆驼案例的对话),Blog
到 blog
在字段选择中。
但让我们更进一步,这是我进行突变的方式。
class AddressInput(graphene.InputObjectType):
name = graphene.String(required=True)
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
address = AddressInput(required=True)
class CreateNewBlogInput(graphene.InputObjectType):
person = PersonInput(required=True)
text = graphene.String(required=True)
class CreateNewBlogPayload(graphene.ObjectType):
blog = graphene.Field(BlogType, required=True)
class CreateNewBlog(graphene.Mutation):
class Arguments:
input_data = CreateNewBlogInput(required=True, name="input")
Output = CreateNewBlogPayload
@staticmethod
def mutate(root, info, input_data):
address = Address.objects.create(name=input_data.person.address.name)
person = Person.objects.create(address=address, name=input_data.person.name)
blog = Blog.objects.create(person=person, text=input_data.text)
blog.save()
return CreateNewBlogPayload(blog=blog)
在构造 Graphene 的变异对象时,我也会将 CreateNewBlog
更改为 createNewBlog
,因为 GraphQL 惯例是使用小驼峰式大小写进行变异。
然后你会 运行 像这样:
mutation {
createNewBlog(
input: {
person: {
address: {
name: "aaa"
},
name: "First Last"
}
text: "hi hi"
}
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
为什么要将整个输入包装在一个输入字段中?主要是因为它使得在使用变量时在客户端更容易调用突变,你可以只提供正确形状的单个输入 arg 而不是多个。
// So instead of this
mutation OldCreateNewBlog($person: PersonInput, $text: String) {
createNewBlog(
personData: $person
text: $text
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
// You have this
mutation NewCreateNewBlog($input: CreateNewBlogInput!) {
createNewBlog(
input: $input
) {
blog {
person {
name
address {
name
}
}
text
}
}
}
后者可以更轻松地随时间更改输入形状,并且只需在客户端代码中的一个地方进行更改。