最近公司要记录用户行为记录日志用到了mongodb,项目框架用的是django rest framework, 用mongodb作为用户行为日志记录数据库导致了django-filter的过滤和drf的search功能都无法使用。网上没能查到解决方法,只能自己写一个过滤和搜索了。
下面展示一些 过滤搜索代码,主要是过滤start_time和end_time字段,并对title, user等字段进行搜索。
class BehaviorLogView(generics.ListAPIView):
"""用户行为记录"""
# permission_classes = ["IsAdmin"]
serializer_class = BehaviorLogTSerializer
filter_backends = (OrderingFilter, )
ordering = ("-update_time",)
search_fields = ("user", "module", "title")
def get_queryset(self):
# self.filter_queryset()
return UserBehavior.objects.all()
def filter_queryset(self, queryset):
params = self.request.query_params
start_time = params.get("start_time")
end_time = params.get("end_time")
search = params.get("search")
if start_time and is_valid_date(start_time):
queryset = queryset.filter(create_time__gte=start_time)
if end_time and is_valid_date(end_time):
end_time = end_time + timedelta(days=1)
queryset = queryset.filter(create_time__lt=end_time)
# 主要看这里(通过Q方法的拼接方法将search_fields总的字段用or逻辑查询是否包含搜索的内容)
if search:
q1 = Q()
q1.connector = 'OR'
for field in self.search_fields:
q1.children.append((field, search))
queryset = queryset.filter(q)
LOGGING.info(q)
return queryset
def get(self, request, *args, **kwargs):
res = self.list(request, *args, **kwargs)
return Response(data={'code': 200, 'msg': 'ok,获取成功!', 'data': res.data})
结果报一下错误: mongoengine.errors.InvalidQueryError: Not a query object:XXXXXXXXXXXXX
猜想是mongoengine中Q函数的使用跟django内置的Q函数有差异导致出错,于是使用mongoengine的Q函数重写
代码
from mongoengine import Q
# from django.db.models import Q
def filter_queryset(self, queryset):
params = self.request.query_params
start_time = params.get("start_time")
end_time = params.get("end_time")
search = params.get("search")
if start_time and is_valid_date(start_time):
queryset = queryset.filter(create_time__gte=start_time)
if end_time and is_valid_date(end_time):
end_time = end_time + timedelta(days=1)
queryset = queryset.filter(create_time__lt=end_time)
# mongoengine中的Q方法无法使用上面的拼接方法,只能自己拼接
if search:
q = Q()
for field in self.search_fields:
q = q | Q(field=search)
queryset = queryset.filter(q)
LOGGING.info(q)
return queryset
大家发现问题没有,上面这句代码q = q | Q(field=search)中python把field当成关键字参数的键了,我们想
实现的应该是q = q | Q(user=search),q = q | Q(module=search),q = q | Q(title=search),这个
问题比较抽象,不好表达。所以网上也找不到解决办法。最后我想起了字典的拆包解包运算符 "**",完美解决
def filter_queryset(self, queryset):
params = self.request.query_params
start_time = params.get("start_time")
end_time = params.get("end_time")
search = params.get("search")
if start_time and is_valid_date(start_time):
queryset = queryset.filter(create_time__gte=start_time)
if end_time and is_valid_date(end_time):
end_time = end_time + timedelta(days=1)
queryset = queryset.filter(create_time__lt=end_time)
# 通过先封装成一个字典再用**运算符解包,完美解决
if search:
q = Q()
for field in self.search_fields:
zip_field = {field+'__icontains': search}
q = q | Q(**zip_field)
queryset = queryset.filter(q)
LOGGING.info(q)
return queryset