|
9 | 9 | IncorrectLookupParameters,
|
10 | 10 | )
|
11 | 11 | from django.contrib.admin.views.main import ChangeList
|
| 12 | +from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation |
12 | 13 | from django.core.paginator import InvalidPage
|
13 |
| -from django.db.models import OrderBy |
| 14 | +from django.db.models import OrderBy, OuterRef, Exists |
14 | 15 | from django.utils.translation import gettext
|
15 | 16 | from django.utils.dateparse import parse_datetime
|
16 | 17 |
|
@@ -408,4 +409,72 @@ def get_typesense_results(self, request):
|
408 | 409 | def get_queryset(self, request):
|
409 | 410 | # this is needed for admin actions that call cl.get_queryset
|
410 | 411 | # exporting is the currently possible way of getting records from typesense without pagination
|
411 |
| - return super().get_queryset(request) |
| 412 | + # Typesense team will work on a flag to disable pagination, until then, we need a way to get this to work. |
| 413 | + # Problem happens when django finds fields only present on typesense in its filter i.e IncorrectLookupParameters |
| 414 | + # First, we collect all the declared list filters. |
| 415 | + ( |
| 416 | + self.filter_specs, |
| 417 | + self.has_filters, |
| 418 | + remaining_lookup_params, |
| 419 | + filters_may_have_duplicates, |
| 420 | + self.has_active_filters, |
| 421 | + ) = self.get_filters(request) |
| 422 | + # Then, we let every list filter modify the queryset to its liking. |
| 423 | + qs = self.root_queryset |
| 424 | + |
| 425 | + for filter_spec in self.filter_specs: |
| 426 | + new_qs = filter_spec.queryset(request, qs) |
| 427 | + if new_qs is not None: |
| 428 | + qs = new_qs |
| 429 | + |
| 430 | + lookup_params_keys = remaining_lookup_params.keys() |
| 431 | + model_field_names = set((local_field.name for local_field in self.model._meta.local_fields)) |
| 432 | + for key in lookup_params_keys: |
| 433 | + if key not in model_field_names: |
| 434 | + # means k only available in typesense |
| 435 | + value = remaining_lookup_params.pop(key) |
| 436 | + new_lookup_params = self.model.collection_class.get_django_lookup(key, value) |
| 437 | + remaining_lookup_params.update(new_lookup_params) |
| 438 | + |
| 439 | + try: |
| 440 | + # Finally, we apply the remaining lookup parameters from the query |
| 441 | + # string (i.e. those that haven't already been processed by the |
| 442 | + # filters). |
| 443 | + qs = qs.filter(**remaining_lookup_params) |
| 444 | + except (SuspiciousOperation, ImproperlyConfigured): |
| 445 | + # Allow certain types of errors to be re-raised as-is so that the |
| 446 | + # caller can treat them in a special way. |
| 447 | + raise |
| 448 | + except Exception as e: |
| 449 | + # Every other error is caught with a naked except, because we don't |
| 450 | + # have any other way of validating lookup parameters. They might be |
| 451 | + # invalid if the keyword arguments are incorrect, or if the values |
| 452 | + # are not in the correct type, so we might get FieldError, |
| 453 | + # ValueError, ValidationError, or ?. |
| 454 | + raise IncorrectLookupParameters(e) |
| 455 | + |
| 456 | + # Apply search results |
| 457 | + qs, search_may_have_duplicates = self.model_admin.get_search_results( |
| 458 | + request, |
| 459 | + qs, |
| 460 | + self.query, |
| 461 | + ) |
| 462 | + |
| 463 | + # Set query string for clearing all filters. |
| 464 | + self.clear_all_filters_qs = self.get_query_string( |
| 465 | + new_params=remaining_lookup_params, |
| 466 | + remove=self.get_filters_params(), |
| 467 | + ) |
| 468 | + # Remove duplicates from results, if necessary |
| 469 | + if filters_may_have_duplicates | search_may_have_duplicates: |
| 470 | + qs = qs.filter(pk=OuterRef("pk")) |
| 471 | + qs = self.root_queryset.filter(Exists(qs)) |
| 472 | + |
| 473 | + # Set ordering. |
| 474 | + ordering = self.get_ordering(request, qs) |
| 475 | + qs = qs.order_by(*ordering) |
| 476 | + |
| 477 | + if not qs.query.select_related: |
| 478 | + qs = self.apply_select_related(qs) |
| 479 | + |
| 480 | + return qs |
0 commit comments