Django Rest Framework如何按模型自定义property属性blog_views实现排序
实现方案
你当前定义的blog_views是Python层面的计算属性,数据库无法直接识别用于排序,最优实现分两种场景,可按需选择:
方案一:无需修改模型,用annotate聚合(适合中小流量站点)
该方案无需改动模型结构,直接在视图层通过数据库聚合实现排序,同时能解决原属性带来的N+1查询问题。
步骤1:修改视图代码
首先导入Django的聚合函数Count,重写视图查询集,新增ordering_fields允许按浏览量排序:
from django.db.models import Count class PostList(generics.ListCreateAPIView): """Blog post lists""" # 聚合统计关联的address_views数量,生成可用于排序的blog_views字段 queryset = Post.objects.annotate( blog_views=Count('address_views', distinct=True) ).all() serializer_class = serializers.PostSerializer authentication_classes = (JWTAuthentication,) permission_classes = (PostsProtectOrReadOnly, IsMentorOnly) filter_backends = [filters.SearchFilter, filters.OrderingFilter] search_fields = ['title', 'body', 'tags__name', 'owner__email', 'owner__username' ] # 新增允许排序的字段,包含我们聚合出来的blog_views ordering_fields = ['created', 'title', 'blog_views']
步骤2:可选优化
因为现在查询集已经通过聚合提前算出了每个Post的浏览量,原Python层面的blog_views property可以直接删除,既避免属性命名冲突,也能解决原来每次调用property都会触发一次数据库查询的N+1问题。
使用方式
启动服务后直接在URL加排序参数即可:
- 按浏览量从低到高排序:
接口地址?ordering=blog_views - 按浏览量从高到低排序:
接口地址?ordering=-blog_views
方案二:新增缓存字段(适合高流量站点,性能最优)
如果站点访问量较大,每次请求都做聚合统计会给数据库带来明显压力,可以在模型中新增一个实际存储浏览量的字段,每次有新的访问时直接更新该字段,排序时直接走普通字段索引,性能最高。
步骤1:修改模型新增字段
class Post(TimeStampedModel, models.Model): # 原有字段保持不变 blog_views = models.IntegerField(_("浏览量"), default=0, db_index=True) # 加索引提升排序性能 # 原有的@property blog_views直接删除
执行makemigrations和migrate将字段同步到数据库。
步骤2:更新访问计数逻辑
在原有新增访问记录(往address_views添加IP)的逻辑里,同步加上post.blog_views +=1的更新逻辑即可。
步骤3:修改视图配置
不需要再做annotate操作,直接把blog_views加入ordering_fields即可:
class PostList(generics.ListCreateAPIView): queryset = Post.objects.all() # 其他配置保持不变 ordering_fields = ['created', 'title', 'blog_views']
内容的提问来源于stack exchange,提问作者Lutaaya Huzaifah Idris
相关产品推荐
相关产品推荐

