使用Django REST Framework创建Bookmark模型实例时遇到问题
使用Django REST Framework实现通用书签系统
我最近做了一个支持给任意内容类型(比如Book、Article这类模型)添加书签的功能,用Django的通用关系配合DRF实现,分享下我的代码和思路:
模型定义(models.py)
首先是核心的Bookmark模型,通过GenericForeignKey实现对任意模型的关联:
from django.db import models from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class Bookmark(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') class Meta: # 限制同一个用户不能重复书签同一个内容 unique_together = ('user', 'content_type', 'object_id')
API层实现(api.py)
接下来是DRF的序列化器和视图集,处理书签的增删改查逻辑:
from django.contrib.contenttypes.models import ContentType from rest_framework import viewsets, mixins, serializers, permissions from .models import Bookmark class BookmarkSerializer(serializers.ModelSerializer): class Meta: model = Bookmark fields = ('id', 'user', 'content_type', 'object_id') # 用户字段设为只读,避免前端手动指定 read_only_fields = ('user',) class BookmarkViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): queryset = Bookmark.objects.all() serializer_class = BookmarkSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly] def perform_create(self, serializer): # 从查询参数获取内容类型(格式如`books.book`)和对象ID content_type_param = self.request.query_params.get('content_type') object_id = self.request.query_params.get('object_id') if not content_type_param or not object_id: raise serializers.ValidationError("必须提供content_type和object_id参数") try: app, model = content_type_param.split(".") ct = ContentType.objects.get_by_natural_key(app, model) # 可选:验证目标内容确实存在 ct.get_object_for_this_type(id=object_id) except (ValueError, ContentType.DoesNotExist, ct.model_class().DoesNotExist): raise serializers.ValidationError("无效的content_type或object_id") serializer.save(user=self.request.user, content_type=ct, object_id=object_id) def get_queryset(self): # 默认只返回当前用户的书签 queryset = Bookmark.objects.filter(user=self.request.user) content_type = self.request.query_params.get('content_type', None) # 支持按内容类型筛选,比如只查看书签过的书籍 if content_type: try: app, model = content_type.split(".") ct = ContentType.objects.get_by_natural_key(app, model) queryset = queryset.filter(content_type=ct) except (ValueError, ContentType.DoesNotExist): # 参数无效时忽略筛选条件,返回用户所有书签 pass return queryset
实用优化建议
- 展示关联内容详情:如果需要在书签列表里显示对应的内容(比如Book的标题、作者),可以给序列化器添加
SerializerMethodField,根据不同模型类型调用对应的序列化器来序列化content_object - 增强权限控制:在
perform_create中可以额外添加逻辑,验证用户有权限访问要书签的内容 - 路由配置:记得在项目的
urls.py中注册视图集,比如用DefaultRouter:from rest_framework.routers import DefaultRouter from .api import BookmarkViewSet router = DefaultRouter() router.register(r'bookmarks', BookmarkViewSet) urlpatterns = router.urls
内容的提问来源于stack exchange,提问作者apiljic
相关产品推荐
相关产品推荐

