You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Graphene含外键与多对多关系模型的Mutation失效问题

Django GraphQL 创建Post Mutation失效问题

Django应用中定义了Tag和Post两个模型,Tag的Mutation可正常工作,数据查询功能也运行正常,但执行创建Post的Mutation后返回post: null,操作失效。

代码片段

models.py

from django.db import models
from django.utils.translation import gettext as _
from django.conf import settings
from django.contrib.auth import get_user_model

User = get_user_model()
# Create your models here.

class Tag(models.Model):
  name    = models.CharField(_("Tag Name"), max_length=50, unique=True)
  
  def __str__(self):
    return self.name  


class Post(models.Model):
  title           = models.CharField(_("Title"), max_length=50, unique=True)
  slug            = models.SlugField(_("Post Slug"), unique=True)
  body            = models.TextField(_("Post Content"))
  createdAt       = models.DateTimeField(auto_now_add=True)
  updatedAt       = models.DateTimeField(auto_now=True)
  published       = models.BooleanField(_("Published"), default=False)
  author          = models.ForeignKey(User, verbose_name=_("Author"), on_delete=models.CASCADE)
  tags            = models.ManyToManyField("Tag", verbose_name=_("Tags"), blank=True)

  class Meta:
    ordering = ['-createdAt']
  
  def __str__(self):
    return self.title

schema.py

import graphene
from graphene_django import DjangoObjectType
from django.contrib.auth import get_user_model
from .models import Post, Tag

User = get_user_model()

class PostType(DjangoObjectType):
  class Meta:
   model = Post

class TagType(DjangoObjectType):
  class Meta:
   model = Tag
   fields = ('id', 'name',)

class TagInput(graphene.InputObjectType):
   name = graphene.String(required=True)
   
class CreatePostMutation(graphene.Mutation):
   class Arguments:
      title    = graphene.String()
      body     = graphene.String()
      published  = graphene.Boolean()
      author_id  = graphene.ID()
      tags     = graphene.List(graphene.Int) 
   
   success = graphene.Boolean()
   post = graphene.Field(PostType)
   
   @classmethod
   def mutate(cls, root, info, title, body, published, author_id, tags):
      author = User.objects.get(pk=author_id)
      post_instance = Post(
         title=title,
         body=body,
         published=published,
         author=author,
      )
      post_instance.save(commit=False)

      if tags:
         tag_objects = Tag.objects.filter(pk__in=tags)
         post_instance.tags.set(tag_objects)
      post_instance.save()

      return CreatePostMutation(success=True, post=post_instance)

class CreateTagMutation(graphene.Mutation):
   class Arguments:
      name = graphene.String(required=True)

   tag = graphene.Field(TagType)

   @classmethod
   def mutate(cls, root, info, name):
      tag = Tag(name=name)
      tag.save()
      return CreateTagMutation(tag=tag)

class Mutation(graphene.ObjectType):

   create_post = CreatePostMutation.Field()
   create_tag  = CreateTagMutation.Field()

schema = graphene.Schema(query=Query, mutation=Mutation)

执行结果

{
  "data": {
    "createPost": {
      "post": null
    }
  }
}

问题原因及修复方案

核心问题

  1. 缺失必填字段slug:Post模型中slug是SlugField(unique=True),属于必填字段,但CreatePostMutation的参数中未接收该值,创建Post实例时也未设置,导致Django模型校验失败,无法保存。
  2. save(commit=False)使用错误:多对多关系(tags)需要在实例被保存到数据库(拥有主键ID)后才能关联,commit=False会阻止实例写入数据库,此时设置tags会失败。

修复代码

修改CreatePostMutation的参数和mutate方法:

from django.utils.text import slugify  # 导入slug生成工具

class CreatePostMutation(graphene.Mutation):
   class Arguments:
      title     = graphene.String(required=True)  # 设为必填,匹配模型约束
      body      = graphene.String(required=True)
      published = graphene.Boolean(default=False)
      author_id = graphene.ID(required=True)
      tags      = graphene.List(graphene.Int)
      slug      = graphene.String()  # 可选参数,若不传则自动生成
   
   success = graphene.Boolean()
   post = graphene.Field(PostType)
   
   @classmethod
   def mutate(cls, root, info, title, body, author_id, published=False, tags=None, slug=None):
      author = User.objects.get(pk=author_id)
      
      # 自动生成slug(如果未传入)
      if not slug:
          slug = slugify(title)
      
      # 创建Post实例并直接保存到数据库
      post_instance = Post(
         title=title,
         slug=slug,
         body=body,
         published=published,
         author=author,
      )
      post_instance.save()

      # 实例已保存,关联tags
      if tags:
         tag_objects = Tag.objects.filter(pk__in=tags)
         post_instance.tags.set(tag_objects)

      return CreatePostMutation(success=True, post=post_instance)

额外优化建议

  • 为模型字段的必填约束匹配GraphQL参数的required=True,避免无效请求。
  • 处理User.DoesNotExist和Tag.DoesNotExist异常,返回明确的错误信息,比如:
    from django.core.exceptions import ObjectDoesNotExist
    
    try:
        author = User.objects.get(pk=author_id)
    except ObjectDoesNotExist:
        raise graphene.ValidationError("指定的作者不存在")
    

内容的提问来源于stack exchange,提问作者B14ck Dz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 03:31:37