when I filter - results and list(all_result_ids) match. when I sort - results and list(all_result_ids) match. when I filter, and sort by any column except "phone" and "emaill" - results and list(all_result_ids) do not match. when I filter and sort by "phone" and "emaill" - results and list(all_result_ids) match.
"email", "phone" are the only columns where the fields always have unique values.
What I need to do is: when I filter and sort by any column - results and list(all_result_ids) match in FilesViewSet
Views.py:
@method_decorator(never_cache, name='dispatch')
class FilesViewSet(ListModelMixin, GenericViewSet):
__slots__ = ()
queryset = Upload.objects.all().select_related('source')
serializer_class = FileSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"]
ordering_fields = "__all__"
filterset_class = FilesFilter
def list(self, request, *args, **kwargs):
filters = request.session.get('filters', None)
if filters is not None:
for field, value in filters.items():
request.query_params._mutable = True
request.query_params[field] = value
else:
request.query_params._mutable = True
request.query_params['ordering'] = '-id'
# Check if the query was already sorted
query_already_sorted = request.session.get('query_already_sorted', False)
if not query_already_sorted:
# Get the IDs directly as a QuerySet, not a list
all_result_ids = self.filter_queryset(self.get_queryset()).values_list('id', flat=True)
# Store the sorted IDs in the session instead of the SortedID model
request.session['sorted_ids'] = list(all_result_ids)
# Set a flag to remember we've sorted this query
if not inspect.stack()[1].function == 'post_filter_data':
request.session['query_already_sorted'] = True
limit = int(request.query_params.get('limit', settings.REST_FRAMEWORK['PAGE_SIZE']))
offset = int(request.query_params.get('offset', 0))
page_number = offset // limit + 1
response = super().list(request, *args, **kwargs)
results = response.data['results']
result_ids = [result['id'] for result in results]
selected_ids = set(SelectedID.objects.filter(id__in=result_ids).values_list('id', flat=True))
for i, result in enumerate(results, start=offset):
result['check'] = 1 if result['id'] in selected_ids else 0
result['index'] = i + 1
has_next = response.data.get('next') is not None
response.data.update({
'results': results,
'total': self.paginator.count,
'page_size': settings.REST_FRAMEWORK['PAGE_SIZE'],
'page_number': page_number,
'has_next': has_next,
})
return response
@action(methods=["POST"], detail=False, url_path="filter_data")
def post_filter_data(self, request, *args, **kwargs):
filters = request.data
request.session['filters'] = filters
request.session['query_already_sorted'] = False
request.session.save() # manually save the session
return self.list(request, *args, **kwargs)
@action(methods=["GET"], detail=False, url_path="filter_data")
def filter_data(self, request, *args, **kwargs):
return Response({
'source': self.queryset.order_by('source').values_list('source__name', flat=True).distinct(),
'country': self.queryset.order_by('country').values_list('country', flat=True).distinct(),
'brand': self.queryset.order_by('brand').values_list('brand', flat=True).distinct(),
'src': self.queryset.order_by('src').values_list('src', flat=True).distinct(),
'grouptype': self.queryset.order_by('grouptype').values_list('grouptype', flat=True).distinct(),
})
filters.py:
from functools import reduce
from operator import or_
from django.db.models import Q
import django_filters
from .models import Upload
class CommaSeparatedCharFilter(django_filters.BaseInFilter, django_filters.CharFilter):
def sanitize(self, value_list):
value_list = [v.strip() for v in value_list[0].split(',')]
return [v for v in value_list if v != '']
class FilesFilter(django_filters.rest_framework.FilterSet):
from_registr_date = django_filters.DateTimeFilter(
field_name='date_registr', lookup_expr='gte')
to_registr_date = django_filters.DateTimeFilter(
field_name='date_registr', lookup_expr='lte')
from_upload_date = django_filters.DateTimeFilter(
field_name='date_upload', lookup_expr='gte')
to_upload_date = django_filters.DateTimeFilter(
field_name='date_upload', lookup_expr='lte')
mpc1_gte = django_filters.NumberFilter(
field_name='mpc1', lookup_expr='gte')
mpc1_lte = django_filters.NumberFilter(
field_name='mpc1', lookup_expr='lte')
mpc2_gte = django_filters.NumberFilter(
field_name='mpc2', lookup_expr='gte')
mpc2_lte = django_filters.NumberFilter(
field_name='mpc2', lookup_expr='lte')
mpc3_gte = django_filters.NumberFilter(
field_name='mpc3', lookup_expr='gte')
mpc3_lte = django_filters.NumberFilter(
field_name='mpc3', lookup_expr='lte')
mpc4_gte = django_filters.NumberFilter(
field_name='mpc4', lookup_expr='gte')
mpc4_lte = django_filters.NumberFilter(
field_name='mpc4', lookup_expr='lte')
download_count = django_filters.NumberFilter(
field_name='download_count', lookup_expr='exact')
search_data = django_filters.CharFilter(
method='search_filter', label="Search")
source = CommaSeparatedCharFilter(
field_name='source__name', lookup_expr='in')
country = CommaSeparatedCharFilter(field_name='country', lookup_expr='in')
brand = CommaSeparatedCharFilter(field_name='brand', lookup_expr='in')
src = CommaSeparatedCharFilter(field_name='src', lookup_expr='in')
grouptype = CommaSeparatedCharFilter(field_name='src', lookup_expr='in')
class Meta:
model = Upload
fields = [
'from_registr_date',
'to_registr_date',
'from_upload_date',
'to_upload_date',
'mpc1_gte',
'mpc1_lte',
'mpc2_gte',
'mpc2_lte',
'mpc3_gte',
'mpc3_lte',
'mpc4_gte',
'mpc4_lte',
'source',
'download_count',
'search_data',
'country',
'brand',
'src',
'grouptype',
]
def search_filter(self, queryset, name, value):
"""
This filter will allow you to search all the text fields in your model for matches.
"""
search_fields = ['first_name', 'last_name', 'phone', 'brand',
'country', 'download_person', 'source__name', 'src', 'email', 'grouptype']
queries = [Q(**{f"{f}__icontains": value}) for f in search_fields]
return queryset.filter(reduce(or_, queries))
models.py:
from django.db import models
from django.utils.translation import gettext_lazy as _
class Source(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Upload(models.Model):
source = models.ForeignKey(
Source, on_delete=models.CASCADE, null=True, blank=True)
first_name = models.CharField(max_length=255, null=True, blank=True)
last_name = models.CharField(max_length=255, null=True, blank=True)
country = models.CharField(max_length=150, null=True, blank=True)
phone = models.CharField(max_length=30, null=True, blank=True)
email = models.EmailField(null=True, blank=True)
date_registr = models.DateTimeField(auto_now_add=False, null=True, blank=True)
date_upload = models.DateTimeField(auto_now_add=True, blank=True)
brand = models.CharField(max_length=500, null=True, blank=True)
src = models.CharField(max_length=500, null=True, blank=True)
grouptype = models.CharField(max_length=500, null=True, blank=True)
mpc1 = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True)
mpc2 = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True)
mpc3 = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True)
mpc4 = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True)
download_count = models.PositiveIntegerField(default=0, blank=True)
download_person = models.CharField(
max_length=100000, blank=True, null=True)
data_history = models.ForeignKey(
'DataHistoryM', on_delete=models.CASCADE, related_name='uploads', null=True)
class Meta:
indexes = [
models.Index(fields=['source',]),
models.Index(fields=['first_name',]),
models.Index(fields=['last_name',]),
models.Index(fields=['country',]),
models.Index(fields=['phone',]),
models.Index(fields=['email',]),
models.Index(fields=['date_registr',]),
models.Index(fields=['date_upload',]),
models.Index(fields=['brand',]),
models.Index(fields=['src',]),
models.Index(fields=['grouptype',]),
models.Index(fields=['mpc1',]),
models.Index(fields=['mpc2',]),
models.Index(fields=['mpc3',]),
models.Index(fields=['mpc4',]),
models.Index(fields=['download_count',]),
models.Index(fields=['download_person',]),
]
I tried to use base instead of request.session but it's faster to get it into another view that way.