restframework內置了一些搜索功能,能夠快速的實現搜索數據庫
Django REST framework的各類技巧【目錄索引】django
全部的代碼都是在下面的兩個版原本作的api
django==1.8.8 djangorestframework==3.2.5
咱們常常要作一些查詢的東東,大致有兩種,以下圖: 併發
restframework經過 filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter) 來很輕鬆的完成了這個工做。app
文檔ide
DjangoFilterBackend對應filter_fields屬性,作相等查詢 SearchFilter對應search_fields,對應模糊查詢ui
二者均可以採用filter中使用的 外鍵__屬性的方式來作查詢this
class CoursesView(ListCreateAPIView): filter_backends = (SchoolPermissionFilterBackend, filters.DjangoFilterBackend, filters.SearchFilter) permission_classes = (IsAuthenticated, ModulePermission) queryset = Course.objects.filter(is_active=True).order_by('-id') filter_fields = ('term',) search_fields = ('name', 'teacher', 'school__name') module_perms = ['course.course'] def get_serializer_class(self): if self.request.method in SAFE_METHODS: return CourseFullMessageSerializer else: return CourseSerializer def get_queryset(self): return Course.objects.select_related('school', ).filter( is_active=True, school__is_active=True, term__is_active=True).order_by('-id')
機制是這樣的,首先view調用get_queryset拿到queryset,而後在filter_backends用取到全部的backend進行filter,所以你能夠寫出不少通用的filter_backend而後組合調用,由於django的queryset是隻有真正作list(list(qs))或者get(qs2)或者for in的時候纔會真正hit數據庫,因此這種拼接是沒有任何問題的。.net
class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class GenericAPIView(views.APIView): def filter_queryset(self, queryset): """ Given a queryset, filter it with whichever filter backend is in use. You are unlikely to want to override this method, although you may need to call it either from a list view, or from a custom `get_object` method if you want to apply the configured filtering backend to the default queryset. """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset
我真正想說的是filter,由於默認的filter_field相關的東西文檔說的很是詳細,兒filter class你可能須要一番嘗試。翻譯
首先看view, 當使用filter_class時就不要寫filter_fields了,由於對應的Class中有一個Meta Class的fields屬性代替了filter_fields。
class SchoolsView(ListCreateAPIView): permission_classes = (IsAuthenticated, ModulePermission) filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter) filter_class = SchoolFilter search_fields = ('name', 'contact') module_perms = ['school.school']
看一個省市區的model
class BaseLocation(TimeStampedModel): is_active = models.BooleanField(default=True, db_index=True) class Meta: abstract = True def __unicode__(self): return self.name class Province(BaseLocation): name = models.CharField(max_length=128, db_index=True) class City(BaseLocation): province = models.ForeignKey(Province) name = models.CharField(max_length=255, db_index=True) class District(BaseLocation): city = models.ForeignKey(City) name = models.CharField(max_length=255, db_index=True)
school model
class School(TimeStampedModel): MIDDLE_SCHOOL = 1 COLLEGE = 2 school_choices = ( (MIDDLE_SCHOOL, u"中學"), (COLLEGE, u"高校") ) category = models.SmallIntegerField( choices=school_choices, db_index=True, default=MIDDLE_SCHOOL) name = models.CharField(max_length=255, db_index=True) city = models.ForeignKey(City) ... is_active = models.BooleanField(default=True, db_index=True)
基礎filter見下面的filter,是用來作父類繼承的,由於Meta class中要寫一個model。
解釋下面的兩行,等號左邊的city對應Meta中fields的city,name="city"是指Filter Meta class中對應Model的屬性,lookup_type是指對應的外鍵屬性。其實下面這兩行翻譯過來是model.objects.filter(city__name = fields中city變量傳來的值, city__province__name=fields中province變量傳來的值)
city = django_filters.Filter(name="city", lookup_type='name') province = django_filters.Filter(name="city", lookup_type='province__name')
# -*- coding: utf-8 -*- import django_filters class CityFilter(django_filters.FilterSet): city = django_filters.Filter(name="city", lookup_type='name') province = django_filters.Filter(name="city", lookup_type='province__name') # need to include Meta filed class Meta: fields = ['city', 'province'] class DistrictFilter(django_filters.FilterSet): district = django_filters.Filter(name="district", lookup_type='name') city = django_filters.Filter(name="district", lookup_type='city__name') province = django_filters.Filter(name="district", lookup_type='city__province__name') # need to include Meta filed class Meta: fields = ['city', 'province', 'district']
上面view中用到的SchoolFilter
class SchoolFilter(CityFilter): class Meta: model = School fields = ['category', 'city', 'province']
search fileds使用like作的,因此存在效率問題,若是有併發什麼的需求,請接入其餘搜索