在 Django 中构建 GraphQL API(Graphene 实战)

用 Graphene-Django 给 Django 应用构建 GraphQL API:Schema 定义、查询过滤、Mutation 数据修改,告别 REST 的过度与不足获取。

最佳实践
并发任务的多路径协同插画

直接回答:GraphQL 让客户端精确声明需要的数据,减少传输层的过度获取与不足获取,但不自动消除数据库过量查询;Graphene-Django 把它与 Django ORM 打通,Schema 定义直接映射模型,一个端点服务所有前端。

为什么是 GraphQL

REST 的痛点:移动端列表页只需要 3 个字段,接口却返回 30 个;详情页又要连调 4 个接口拼数据。GraphQL 翻转权力结构——客户端写查询描述要什么,服务端一次返回,前端迭代不再绑架后端发版。

搭建基础

pip install graphene-django
# settings.py
INSTALLED_APPS = [..., "graphene_django"]
GRAPHENE = {"SCHEMA": "blog.schema.schema"}
# urls.py
from django.urls import path
from graphene_django.views import GraphQLView

urlpatterns = [path("graphql", GraphQLView.as_view(graphiql=True))]

graphiql=True 仅作为开发调试。保留 Django CSRF 中间件,Mutation 客户端须携带有效 CSRF token;生产限制 IDE 和接口访问。

定义 Schema

# schema.py
import graphene
from graphene_django import DjangoObjectType
from .models import Author, Book

class AuthorType(DjangoObjectType):
    class Meta:
        model = Author
        fields = ("id", "name")

class BookType(DjangoObjectType):
    class Meta:
        model = Book
        fields = ("id", "title", "price", "author")

class Query(graphene.ObjectType):
    all_books = graphene.List(BookType)
    book = graphene.Field(BookType, id=graphene.Int(required=True))

    def resolve_all_books(root, info):
        return Book.objects.select_related("author").all()[:100]

    def resolve_book(root, info, id):
        return Book.objects.filter(pk=id).first()

schema = graphene.Schema(query=Query)

客户端查询:

query {
  allBooks {
    title
    author { name }
  }
}

一次请求拿到书加作者——resolver 里的 select_related 顺手解决了 N+1。

查询过滤

class Query(graphene.ObjectType):
    books = graphene.List(BookType, title_contains=graphene.String(), max_price=graphene.Float())

    def resolve_books(root, info, title_contains=None, max_price=None):
        qs = Book.objects.all()
        if title_contains:
            qs = qs.filter(title__icontains=title_contains)
        if max_price is not None:
            qs = qs.filter(price__lte=max_price)
        return qs.select_related("author")[:100]

参数即过滤条件,客户端自由组合;上例 100 条限制只是演示,生产应实现有边界的分页和对象级访问控制。进阶场景可以接 django-filter 自动生成过滤集。

Mutation:修改数据

class CreateBook(graphene.Mutation):
    class Arguments:
        title = graphene.String(required=True)
        price = graphene.Float(required=True)
        author_id = graphene.Int(required=True)

    book = graphene.Field(BookType)

    def mutate(root, info, title, price, author_id):
        from graphql import GraphQLError
        from decimal import Decimal
        from django.core.exceptions import ValidationError
        if not info.context.user.is_authenticated or not info.context.user.is_staff:
            raise GraphQLError("无创建权限")
        book = Book(title=title, price=Decimal(str(price)), author_id=author_id)
        try:
            book.full_clean()
        except ValidationError:
            raise GraphQLError("图书字段无效")
        book.save()
        return CreateBook(book=book)

class Mutation(graphene.ObjectType):
    create_book = CreateBook.Field()

schema = graphene.Schema(query=Query, mutation=Mutation)
mutation {
  createBook(title: "新书", price: 59.9, authorId: 1) {
    book { id title }
  }
}

生产注意事项

  • N+1 是 GraphQL 的头号陷阱:resolver 嵌套越深越要检查,配 dataloaders 批量加载;对同一个命名查询记录返回条数、SQL 次数和耗时,比较修改前后是否减少数据库访问。
  • 查询深度与复杂度限制:防止恶意嵌套查询打爆数据库;
  • 鉴权:在 resolver 或中间件里检查 info.context.user。

常见问题(FAQ)

Q:GraphQL 会取代 REST 吗?
A:不会。简单资源型 API 用 REST 更直白;前端需求多变、数据关系复杂、多端消费的场景 GraphQL 优势明显。很多团队两者并存。

Q:Graphene 和 Strawberry 选哪个?
A:Strawberry 用类型注解写 Schema,更现代;Graphene 历史久、与 Django 集成成熟。新项目两者皆可,看团队口味。

Q:文件上传怎么做?
A:GraphQL 规范之外,用 multipart 请求规范(graphene-file-upload)或干脆让上传走独立 REST 端点——务实不丢人。

官方参考

本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台