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

如何以最符合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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:08:41