在 Django + Graphene 中实现 Hackernews 的 Links 与 Votes 关联:从外键建模到投票 Mutation 实战
发布时间:2026/9/25 6:54:12来源:尧图网络
【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载本教程基于 howtographql 仓库的 graphql-python 系列 中的核心章节讲解在 Django 与 Graphene 搭建的 GraphQL 服务器中如何把用户User挂接到链接Link上、为链接添加投票Vote功能并打通三者之间的查询关系。读完本文你将掌握通过settings.AUTH_USER_MODEL建立模型外键、在 Graphene Mutation 中读写当前登录用户、构造带权限校验的CreateVote投票 Mutation以及用DjangoObjectType暴露关联查询。该章节承接 用户认证章节使用django-graphql-jwt实现 Token 认证也为此后章节中的 过滤、分页 与 Relay 重构 打好了数据模型基础。一、整体思路Hackernews 的两大核心功能在系列教程的 Getting Started 章节中已经明确这个 Hackernews 克隆需要具备三大能力用户与链接列表、用户创建与认证、用户创建链接并为其投票。本章解决的正是最后一项让Link记录是谁发布的posted_by外键指向用户新增Vote模型记录哪个用户投了哪个链接提供CreateLink带作者与CreateVote两个 Mutation通过VoteType与votes字段让客户端能查询到投票数据及每个链接的关联投票。在此之前你已经完成了 基础查询LinkType、links查询、基础 MutationCreateLink与 用户认证usersapp、UserType、tokenAuth等 JWT Mutation。本章就是把这些能力串起来让链接归属到用户让用户能对链接投票。二、把用户挂到链接上posted_by外键1. 修改模型引入 Django 内置用户模型认证章节里我们通过django.contrib.auth的get_user_model()创建了用户。要把链接与用户关联最规范的方式不是硬编码自定义用户模型名而是引用settings.AUTH_USER_MODEL这样后续即使替换了自定义用户模型代码也无需改动。在links/models.py顶部导入 Django 设置from django.conf import settings # ...code然后在Link模型末尾追加posted_by字段posted_by models.ForeignKey(settings.AUTH_USER_MODEL, nullTrue, on_deletemodels.CASCADE)这里有两个参数值得注意nullTrue允许posted_by为空。这意味着匿名未登录用户创建链接时该字段可存NULL不会因为缺少用户而报错on_deletemodels.CASCADE当关联的用户被删除时其名下所有链接一并删除保持数据一致性Django 2.x 之后on_delete为必填参数。2. 同步数据库模型变更后需要让 Django 生成迁移并应用到数据库python manage.py makemigrations python manage.py migrate第一条命令扫描模型变化生成迁移文件第二条命令将其写入数据库本系列使用开箱即用的 SQLite。这与 Queries 章节 中创建Link模型后的流程完全一致。3. 改造CreateLink返回发布者接下来修改links/schema.py。先导入认证章节定义的UserTypefrom users.schema import UserType然后把CreateLink改为带posted_by输出的完整版本class CreateLink(graphene.Mutation): id graphene.Int() url graphene.String() description graphene.String() posted_by graphene.Field(UserType) class Arguments: url graphene.String() description graphene.String() def mutate(self, info, url, description): user info.context.user or None link Link( urlurl, descriptiondescription, posted_byuser, ) link.save() return CreateLink( idlink.id, urllink.url, descriptionlink.description, posted_bylink.posted_by, )对比 Mutations 章节 中的原始版本核心变化有两处输出字段新增posted_by graphene.Field(UserType)客户端可以像查询User类型一样自由选择username、email等字段——这正是 认证章节 中强调的返回完整对象而非逐字段拼接mutate方法通过info.context.user读取当前请求的用户。这里的info是 GraphQL 解析上下文info.context.user由django-graphql-jwt的JSONWebTokenMiddleware在认证章节的配置中注入请求携带合法 Token 时它就是当前登录用户未携带时则为AnonymousUser。or None保证匿名用户场景下posted_by写入NULL与模型上的nullTrue呼应。测试时记得带上 Token在 Insomnia 的Header选项卡中添加AUTHORIZATION请求头值为JWT token详见 认证章节的测试部分再发送createLinkMutation。若一切正常返回结果中会包含postedBy对象展示当前用户的字段信息。三、添加投票Vote模型与CreateVoteMutationHackernews 的核心玩法是投票链接被投得越多就越热门。投票涉及两个主体——用户和链接因此需要一张关联表。1. 定义Vote模型在links/models.py中追加class Vote(models.Model): user models.ForeignKey(settings.AUTH_USER_MODEL, on_deletemodels.CASCADE) link models.ForeignKey(links.Link, related_namevotes, on_deletemodels.CASCADE)解读user外键指向用户模型on_deleteCASCADE表示用户删除时投票记录一并删除link外键指向links.Link注意这里使用字符串引用links.Link也是 Django 支持的写法related_namevotes非常关键——它定义了反向关系名使得Link对象可以通过link.votes访问其全部投票记录后续查询章节会用到。同样执行数据库同步python manage.py makemigrations python manage.py migrate2. 定义CreateVoteMutation在links/schema.py中先把模型导入改为同时引入Votefrom links.models import Link, Vote然后添加投票 Mutationclass CreateVote(graphene.Mutation): user graphene.Field(UserType) link graphene.Field(LinkType) class Arguments: link_id graphene.Int() def mutate(self, info, link_id): user info.context.user if user.is_anonymous: raise Exception(You must be logged to vote!) link Link.objects.filter(idlink_id).first() if not link: raise Exception(Invalid Link!) Vote.objects.create( useruser, linklink, ) return CreateVote(useruser, linklink)最后把新 Mutation 注册到根 Mutation 类class Mutation(graphene.ObjectType): create_link CreateLink.Field() create_vote CreateVote.Field()这个 Mutation 展示了 GraphQL 服务端的典型三步校验模式步骤代码作用身份校验user.is_anonymous判断匿名用户禁止投票抛出You must be logged to vote!数据校验Link.objects.filter(idlink_id).first()使用first()而非get()链接不存在时得到None而非抛DoesNotExist随后抛出Invalid Link!落库Vote.objects.create(...)创建投票记录并返回CreateVote(useruser, linklink)作为输出两种异常在当前章节中都通过raise Exception(message)抛出在后续的 Error Handling 章节 中其中一条会被替换为from graphql import GraphQLError的raise GraphQLError(...)——两者都会中止执行并把括号内的消息返回给客户端效果等价。测试时对第一个链接发起createVoteMutation携带 Token成功后会返回投票者用户对象与被投票的链接对象。四、打通查询VoteType与votes字段投票能写入但还看不到——此时需要把投票数据暴露到查询层。1. 定义VoteType在links/schema.py中紧跟LinkType之后添加class VoteType(DjangoObjectType): class Meta: model VoteDjangoObjectType是 Graphene-Django 提供的桥接类型会根据 Django 模型自动生成对应的 GraphQL 字段。由于Vote模型上的user与link都是外键VoteType会天然暴露user和link两个关联对象字段客户端可以嵌套查询谁投了哪个链接。2. 扩展 Query新增votes字段与解析器class Query(graphene.ObjectType): links graphene.List(LinkType) votes graphene.List(VoteType) def resolve_links(self, info, **kwargs): return Link.objects.all() def resolve_votes(self, info, **kwargs): return Vote.objects.all()resolve_votes返回Vote.objects.all()即所有投票记录。查询示例query { votes { id user { username } link { id url } } }3. 反向关联从链接看投票因为模型中设置了related_namevotes客户端还可以从链接一侧反向查询query { links { id url votes { id user { username } } } }这就是Relating Links and Votes一节要达成的目标LinkType借助 Django ORM 的反向关系link.votes自动获得votes字段无需再手写解析器。Graphene-Django 会为模型上的每个关系自动生成可查询字段这种模型即类型的映射正是DjangoObjectType的核心价值。五、本章在完整教程中的位置与延伸投票与关联打通后本系列后续章节都是在此基础上继续演进Error Handling把CreateVote中的raise Exception(You must be logged to vote!)换成raise GraphQLError(...)并演示对无效链接投票时的错误响应FilteringQuery在links/votes基础上增加search参数通过django.db.models.Q实现url__icontains与description__icontains的模糊搜索Pagination为links查询增加first/skip参数用 Python 切片对查询集分页Relay在schema_relay.py中为Link/Vote建立LinkNode/VoteNode并用DjangoFilterConnectionField与graphene.relay.ClientIDMutation重构本节的查询与投票功能。可以看到本章设计的Vote模型、VoteType与外键 related_name的关联方式是整个教程数据层的中枢过滤、分页、Relay 章节都直接复用了这里的模型结构与查询字段。六、小结本节的三个关键设计用settings.AUTH_USER_MODEL而非硬编码用户类保证用户模型可替换性nullTrue让匿名创建的链接合法存在。Mutation 的校验顺序先验身份is_anonymous再验数据first()判空最后落库返回完整对象——这个模式可复用于几乎所有需要用户态的写操作。DjangoObjectTyperelated_name自动打通关联查询VoteType暴露双向关系客户端既可查所有投票也可从link.votes反向看每个链接的投票列表全程无需手写关联解析器。完成本节后你的 Hackernews GraphQL 服务器已经具备完整的发布—投票—查询闭环。继续阅读 Error Handling 章节学习如何用GraphQLError让错误信息更加规范。赞分享【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载相关推荐在 Graphene-Django 中为 GraphQL 查询添加搜索过滤Hackernews 链接搜索实战howtographql Python 教程在 Graphene Django 中为 GraphQL 查询添加搜索过滤Hackernews 链接搜索实战howtographql Python 教程3个场景告诉你为什么KoboldCpp是本地AI模型部署的最佳选择3个场景告诉你为什么KoboldCpp是本地AI模型部署的最佳选择 你是否曾想过在本地运行强大的AI模型可以像打开一个普通软件那样简单KoboldCpp人工智能大模型本地部署模型推理服务Convex 函数编写指南从 query / mutation 到关系数据建模实战Convex 函数编写指南从 query / mutation 到关系数据建模实战 导读 本文以 convex backend 仓库中 relational数据库后端上一篇React-Paginate轻量级的React分页组件下一篇告别繁琐验证validate.js 让前端表单校验如丝般顺滑创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网