如何基于用户is_staff状态过滤Django序列化器中的嵌套Book对象
解决方案
方法一:通过序列化器的SerializerMethodField实现过滤
这种方式直接在序列化器中根据用户权限动态过滤书籍,实现简单直观:
修改BookCategorySerializer,将固定的books字段替换为SerializerMethodField,然后在对应的方法里根据当前用户的is_staff状态过滤书籍:
class BookCategorySerializer(serializers.ModelSerializer): """ Serialize books by category """ books = serializers.SerializerMethodField() class Meta: model = BookCategory fields = ( 'name', 'active', 'books', ) def get_books(self, obj): # 从序列化器上下文获取当前请求的用户 user = self.context['request'].user # 根据用户状态过滤书籍 if user.is_staff: books = obj.book_set.all() else: books = obj.book_set.filter(active=True) # 使用BookSerializer序列化过滤后的书籍 return BookSerializer(books, many=True).data
视图集不需要修改,ReadOnlyModelViewSet默认会将request传入序列化器的context中,供我们获取用户信息。
方法二:在视图集中通过预取关联优化性能
如果你的书籍数据量较大,方法一可能会导致N+1查询问题(每个分类单独查询一次书籍)。这种情况下,建议在视图集的get_queryset方法中,结合Prefetch对象动态预取过滤后的书籍,提升性能:
首先修改视图集:
from django.db.models import Prefetch class BookCategoryViewSet(viewsets.ReadOnlyModelViewSet): """ View available books by category """ serializer_class = BookCategorySerializer def get_queryset(self): user = self.request.user # 根据用户状态定义预取的书籍查询集 if user.is_staff: book_prefetch = Prefetch('book_set', queryset=Book.objects.all()) else: book_prefetch = Prefetch('book_set', queryset=Book.objects.filter(active=True)) # 预取过滤后的书籍,避免N+1查询 return BookCategory.objects.all().prefetch_related(book_prefetch)
然后保持原来的BookCategorySerializer不变即可:
class BookCategorySerializer(serializers.ModelSerializer): """ Serialize books by category """ books = BookSerializer(many=True, read_only=True, source='book_set') class Meta: model = BookCategory fields = ( 'name', 'active', 'books', )
这种方式提前在数据库层面完成了书籍的过滤和预取,性能更优,适合数据量较大的场景。
内容的提问来源于stack exchange,提问作者Jake Mulhern
相关产品推荐
相关产品推荐

