如何以最符合Django风格实现复杂外键关系的DRF增删改查API
Django + DRF 实现博客与多作者关联的最佳实践
嘿,这个需求其实是Django + DRF里非常常见的多对多关联场景,我来给你捋捋最符合Django风格的实现方式,保证既优雅又高效~
第一步:模型层设计(核心是多对多关联)
首先要把博客和作者的多对多关系定义清楚,Django的ManyToManyField就是专门干这个的,记得设置related_name方便反向查询(比如通过作者找他参与的所有博客):
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) # 其他字段比如邮箱、简介等按需添加 class Blog(models.Model): title = models.CharField(max_length=200) content = models.TextField() # 定义多对多关联,related_name让作者可以通过.blogs访问关联博客 authors = models.ManyToManyField(Author, related_name='blogs') # 其他字段比如发布时间、分类等按需添加
第二步:序列化器处理(分两种场景)
DRF的序列化器是处理API数据交互的核心,针对多对多关系,有两种常用实现方式,按需选择:
场景1:仅返回作者ID(高效简洁)
如果客户端只需要作者的ID来关联,用PrimaryKeyRelatedField就足够了,这种方式性能最好,适合数据量大的场景:
from rest_framework import serializers from .models import Blog, Author class BlogSerializer(serializers.ModelSerializer): authors = serializers.PrimaryKeyRelatedField(many=True, queryset=Author.objects.all()) class Meta: model = Blog fields = ['id', 'title', 'content', 'authors']
场景2:嵌套返回作者详情(直观友好)
如果客户端需要直接拿到作者的详细信息(比如姓名、简介),可以用嵌套序列化器,但要注意默认嵌套序列化器不支持创建/更新,需要手动重写create和update方法:
class AuthorSerializer(serializers.ModelSerializer): class Meta: model = Author fields = ['id', 'name'] # 按需添加字段 class BlogSerializer(serializers.ModelSerializer): authors = AuthorSerializer(many=True, read_only=False) class Meta: model = Blog fields = ['id', 'title', 'content', 'authors'] def create(self, validated_data): # 先取出作者数据,因为Blog模型不能直接接收authors字典 authors_data = validated_data.pop('authors') # 创建博客实例 blog = Blog.objects.create(**validated_data) # 关联作者:用get_or_create避免创建重复作者 for author_data in authors_data: author, _ = Author.objects.get_or_create(**author_data) blog.authors.add(author) return blog def update(self, instance, validated_data): # 取出作者数据(如果有的话) authors_data = validated_data.pop('authors', None) # 更新博客基本字段 instance.title = validated_data.get('title', instance.title) instance.content = validated_data.get('content', instance.content) instance.save() # 更新作者关联:先清空再重新添加(也可以按需增量更新) if authors_data: instance.authors.clear() for author_data in authors_data: author, _ = Author.objects.get_or_create(**author_data) instance.authors.add(author) return instance
第三步:视图层实现(用ModelViewSet省代码)
DRF的ModelViewSet会自动帮你实现CRUD接口,非常符合Django的“不要重复造轮子”理念,记得用prefetch_related优化查询,避免N+1问题:
from rest_framework import viewsets from .models import Blog from .serializers import BlogSerializer class BlogViewSet(viewsets.ModelViewSet): # 预取authors关联数据,优化查询性能 queryset = Blog.objects.all().prefetch_related('authors') serializer_class = BlogSerializer
第四步:URL配置(用Router简化)
用DRF的DefaultRouter可以自动生成RESTful风格的URL,不用手动写一堆路径:
from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import BlogViewSet router = DefaultRouter() router.register(r'blogs', BlogViewSet) urlpatterns = [ path('', include(router.urls)), ]
额外优化:自定义Action管理单个作者关联
如果需要单独给博客添加/删除作者(而不是一次性替换所有作者),可以给ViewSet添加自定义Action,更符合RESTful设计:
from rest_framework.decorators import action from rest_framework.response import Response class BlogViewSet(viewsets.ModelViewSet): # ... 之前的代码 @action(detail=True, methods=['post']) def add_author(self, request, pk=None): """给指定博客添加单个作者""" blog = self.get_object() author_id = request.data.get('author_id') try: author = Author.objects.get(id=author_id) blog.authors.add(author) return Response({'status': '作者已成功添加'}) except Author.DoesNotExist: return Response({'error': '作者不存在'}, status=404) @action(detail=True, methods=['delete']) def remove_author(self, request, pk=None): """从指定博客移除单个作者""" blog = self.get_object() author_id = request.data.get('author_id') try: author = Author.objects.get(id=author_id) blog.authors.remove(author) return Response({'status': '作者已成功移除'}) except Author.DoesNotExist: return Response({'error': '作者不存在'}, status=404)
这样就可以通过POST /blogs/{pk}/add_author/和DELETE /blogs/{pk}/remove_author/来单独管理作者关联了。
内容的提问来源于stack exchange,提问作者Why
相关产品推荐
相关产品推荐

