我正在尝试使我的自定义过滤器和排序后端与Django rest框架中的默认搜索后端一起使用。过滤和排序可以很好地相互配合,但是当查询中包含搜索并且我试图按对象名称对查询进行排序时,就会发生数据重复。
我尝试打印查询和查询大小,但是将其记录在过滤器中时似乎还可以,但是在响应中我有不同的对象计数(例如,过滤器查询中有79个对象,最终结果中有170个重复的对象)
这是我的过滤器集类
class PhonesFilterSet(rest_filters.FilterSet):
brands = InListFilter(field_name='brand__id')
os_ids = InListFilter(field_name='versions__os')
version_ids = InListFilter(field_name='versions')
launched_year_gte = rest_filters.NumberFilter(field_name='phone_launched_date__year', lookup_expr='gte')
ram_gte = rest_filters.NumberFilter(field_name='internal_memories__value', method='get_rams')
ram_memory_unit = rest_filters.NumberFilter(field_name='internal_memories__units', method='get_ram_units')
def get_rams(self, queryset, name, value):
#here is the problem filter
#that not works with ordering by name
q=queryset.filter(Q(internal_memories__memory_type=1) & Q(internal_memories__value__gte=value))
print('filter_set', len(q))
print('filter_set_query', q.query)
return q
def get_ram_units(self, queryset, name, value):
return queryset.filter(Q(internal_memories__memory_type=1) & Q(internal_memories__units=value))
class Meta:
model = Phone
fields = ['brands', 'os_ids', 'version_ids', 'status', 'ram_gte']
我的订购班:
class CustomFilterBackend(filters.OrderingFilter):
allowed_custom_filters = ['ram', 'camera', 'year']
def get_ordering(self, request, queryset, view):
params = request.query_params.get(self.ordering_param)
if params:
fields = [param.strip() for param in params.split(',')]
ordering = [f for f in fields if f in self.allowed_custom_filters]
if ordering:
return ordering
# No ordering was included, or all the ordering fields were invalid
return self.get_default_ordering(view)
def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view)
if ordering:
if 'ram' in ordering:
max_ram = Max('internal_memories__value', filter=Q(internal_memories__memory_type=1))
queryset = queryset.annotate(max_ram=max_ram).order_by('-max_ram')
elif 'camera' in ordering:
max_camera = Max('camera_pixels__megapixels', filter=Q(camera_pixels__camera_type=0))
queryset = queryset.annotate(max_camera=max_camera).order_by('-max_camera')
elif 'year' in ordering:
queryset = queryset.filter(~Q(phone_released_date=None)).order_by('-phone_released_date__year')
elif 'name' in ordering:
#here is the problem ordering
#thats not working with filter
#with one to many relations
queryset = queryset.order_by('-brand__name', '-model__name')
return queryset
Viewset类:
class PhoneViewSet(viewsets.ModelViewSet):
queryset = Phone.objects.all()
serializer_class = PhoneSerializer
filter_backends = (filters.SearchFilter, CustomFilterBackend, django_filters.rest_framework.DjangoFilterBackend)
search_fields = ('brand__name', 'model__name')
ordering_fields = ('brand__name', 'model__name')
filter_class = PhonesFilterSet
因此,当我应用带有过滤器和搜索的订购时,我期望没有数据重复。我的问题是,为什么过滤器和响应中的对象数量不同,而数据却在其中重复?我不知道从哪里开始调试。预先感谢。
答案 0 :(得分:0)
使用distinct()
应该可以解决此问题:
返回在SQL查询中使用
QuerySet
的新SELECT DISTINCT
。这样可以从查询结果中消除重复的行。默认情况下,
QuerySet
不会消除重复的行。实际上,这很少有问题,因为诸如Blog.objects.all()
之类的简单查询不会带来重复结果行的可能性。但是,如果您的查询跨越多个表,则在评估QuerySet
时可能会得到重复的结果。那是您使用distinct()
的时候。
但是请注意,您仍然可能会得到重复的结果:
order_by()
列中包含SQL SELECT
调用中使用的任何字段。与distinct()
结合使用有时会导致意外结果。如果按相关模型中的字段排序,则这些字段将添加到选定的列中,否则它们可能使重复的行看起来是不同的。由于多余的列不会出现在返回的结果中(它们仅用于支持排序),因此有时看起来好像正在返回不明显的结果。
https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.query.QuerySet.distinct
如果使用的是PostgreSQL,则可以指定DISTINCT
应该应用的字段名称。这可能会有所帮助。 (我不确定。)有关此的更多信息,请参见上面的链接。
所以,我会return queryset.distinct()
用在您认为遇到问题的方法中。我不会总是应用它(就像我在上面的注释中写的那样,用于调试),因为对于简单的查询您不需要它。