Fork me on GitHub

Python系列之《Django-DRF-搜索》

在GenericAPIView类里有个get_queryset()方法,重写此方法允许以多种不同方式自定义视图返回的查询集。

例如:搜索用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UserViewset(viewsets.ReadOnlyModelViewSet):
"""
retrieve:
返回指定用户信息
list:
返回用户列表
"""
queryset = User.objects.all()
serializer_class = UserSerializer

def get_queryset(self):
queryset = super(UserViewset, self).get_queryset()
username = self.request.query_params.get("username", "")
if username:
queryset = queryset.filter(username__icontains=username)
return queryset

请求:

1
GET http://127.0.0.1:8000/users/?username=admin

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 96,
"username": "admin",
"email": "tanshuai@nicksors.cc"
}
]
}

而直接更改get_queryset这个方法来实现搜索功能,是比较原始的。项目中一般不会使用这种方法,而是使用Django-Filter来实现。

GitHub项目地址:https://github.com/carltongibson/django-filter

安装

使用pip安装即可

1
pip install django-filter

在setting.py文件里的INSTALLED_APPS配置下添加django_filters

1
2
3
4
INSTALLED_APPS = [
...
'django_filters',
]

使用

创建过滤器

如果你有多个App,需要在每个App目录下创建filters文件,然后写好过滤器,提供该App使用。
这里以User App为例,创建过滤器内容如下:

vim users/filters.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib.auth import get_user_model
User = get_user_model()
import django_filters


class UserFilter(django_filters.FilterSet):
"""
用户过滤器类
"""
username = django_filters.CharFilter(name="username", lookup_expr='icontains')

class Meta:
model = User
fields = ['username', ]

技术点说明:

  1. 定义一个用户过滤器类,该类继承django_filters的FilterSet子类;
  2. django_filters这里使用了CharFilte,其实还有很多其他Filte,看看源码不难发现;
  3. 参数:name 指定搜索字段,lookup_expr 指定匹配规则(强制匹配或是模糊匹配等)
  4. Meta 里的fields字段,是指定通过什么字段来进行匹配搜索结果。

使用搜索功能

1)views视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django_filters.rest_framework import DjangoFilterBackend
from django.contrib.auth import get_user_model
from .filters import UserFilter
User = get_user_model()

class UserViewset(viewsets.ReadOnlyModelViewSet):
"""
retrieve:
返回指定用户信息
list:
返回用户列表
"""
serializer_class = UserSerializer
filter_backends = (DjangoFilterBackend, )
filter_class = UserFilter
filter_fields = ("username",)

  1. 导入DjangoFilterBackend,作为filter_backends的参数,是固定写法;
  2. filter_class 调用我们上面写的用户过滤器类,使用我们自己写的规则来进行过滤;
  3. filter_fields 指定哪个字段进行搜索。

2)页面搜索

在API页面上首先会出现 “过滤器” 点击按钮,点击后可以通过我们定义好的字段进行搜索,返回相应结果。

配置全局搜索

配置全局搜索的好处不言而喻,本着不希望在viewset里写过多的代码(而且是重复的),那可以在setting.py文件配置如下:

1
2
3
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}

搜索效果一样,不一样的是你不用再每个viewset里写filter_backends = (DjangoFilterBackend, )这个配置了。

高级搜索: Q

使用Django自带的Q来进行规则匹配,达到高级搜索需求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import django_filters
from django.db.models import Q
from .models import Idc


class IdcFilter(django_filters.FilterSet):
"""
IDC机房过滤器类
"""
name = django_filters.CharFilter(method='search_idc')

def search_idc(self, queryset, name, value):
return queryset.filter(Q(letter__icontains=value) | Q(name__icontains=value))

class Meta:
model = Idc
fields = ['name', ]

可以按照 letter 字段和 name 字段来进行搜索,都能搜出想要的结果。

到这里,搜索功能也就做完了,不足之处多多指教,感谢关注。

-------------本文结束感谢您的阅读-------------