wagtail-nest / wagtail-modeladmin

Add any model in your project to the Wagtail admin. Formerly wagtail.contrib.modeladmin.
Other
22 stars 8 forks source link

WagtailBackendSearchHandler ordering error with a Page descendant model #22

Open roodie opened 4 years ago

roodie commented 4 years ago

Issue Summary

Due to some client requirement, I had to add a ModelAdmin for one of our custom Page types to our Wagtail-based site.

The class itself is very simple:

class NewsPage(Page):
    subtitle = models.CharField(max_length=200, blank=True, null=True)
    body = StreamField(CONTENT_STREAMFIELD_ELEMENTS, blank=True, null=True)
    author = models.CharField(max_length=200, blank=True, null=True)
    date = models.DateField(verbose_name="Display date", blank=True, null=True)
    is_sticky = models.BooleanField(default=False)
    is_on_index = models.BooleanField(default=False)

The date, is_sticky and is_on_index fields are added to she search_fields as filters.

The ModelAdmin page is similarly simple:

class NewsAdmin(ModelAdmin):
    model = NewsPage
    list_display = ('title', 'url_path', 'date', 'first_published_at', 'is_sticky', 'is_on_index', 'live')
    list_filter = (('date', DateRangeFilter), 'is_sticky', 'is_on_index', 'live')
    search_handler_class = WagtailBackendSearchHandler

The search itself works properly; however, if I click on any of the field headers to sort a queryset, I get the following error:

wagtail.search.backends.base.OrderByFieldError: Cannot sort search results with field "page_ptr". Please add index.FilterField('page_ptr') to NewsPage.search_fields.

It doesn't matter if I try to order by a specific NewsPage field, or ny the default Page.title for example.

Full strace:

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\core\handlers\base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\core\handlers\base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\admin\urls\__init__.py" in wrapper
  105.             return view_func(request, *args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\admin\auth.py" in decorated_view
  165.                     return view_func(request, *args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\contrib\modeladmin\options.py" in index_view
  341.         return view_class.as_view(**kwargs)(request)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\views\generic\base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\utils\decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\contrib\modeladmin\views.py" in dispatch
  254.         self.queryset = self.get_queryset(request)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\contrib\modeladmin\views.py" in get_queryset
  525.         return self.get_search_results(request, qs, self.query)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\contrib\modeladmin\views.py" in get_search_results
  272.         return self.search_handler.search_queryset(queryset, search_term, **kwargs)

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\contrib\modeladmin\helpers\search.py" in search_queryset
  71.             order_by_relevance=not preserve_order,

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\search\backends\base.py" in search
  370.             partial_match=partial_match,

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\search\backends\base.py" in _search
  358.         search_query.check()

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\search\backends\base.py" in check
  161.         list(self._get_order_by())

File "C:\virtualenv\virtualenv_wagtail\lib\site-packages\wagtail\search\backends\base.py" in _get_order_by
  137.                     field_name=field_name

Exception Type: OrderByFieldError at /admin/content/newspage/
Exception Value: Cannot sort search results with field "page_ptr". Please add index.FilterField('page_ptr') to NewsPage.search_fields.

Technical details

Python version: Python 3.6.8 Django version: 2.2.6 Wagtail version: 2..7 Search backend: Postgres (from contrib)

gasman commented 4 years ago

Hi @roodie - thanks for the report. Are you able to replicate this issue independently on a fresh Wagtail installation, please? That would assist us enormously in investigating this further.

roodie commented 4 years ago

Sorry, yes, I was able, the sample code is actually from the fresh Wagtail installation and a separate small project.

https://github.com/roodie/searchtest/tree/master/home

ababic commented 4 years ago

Hi @roodie.

Does adding page_ptr to search_fields as the error says solve the problem for you? There are some limitations to WagtailBackendSearchHandler, which we tried to highlight in the docs:

http://docs.wagtail.io/en/stable/reference/contrib/modeladmin/indexview.html#indexing-extra-fields-using-index-filterfield

This is mostly why we couldn't make it the default option when objects are indexed using the search backend. Whenever a querystring is provided, the search backend needs to know how to parse the full query; including ordering and filters. By default, IndexView orders objects by the primary key (in this case page_ptr) for consistency, which is why you are getting this specific error message.

You may be able to work around this by specifying an alternative default order via the the ordering attribute, but you may face similar errors if ordering or filtering by other fields not known to the search backend.

roodie commented 4 years ago

Does adding page_ptr to search_fields as the error says solve the problem for you?

No, I still get the same error message (that was the first thing I tried when I encountered the error).

You may be able to work around this by specifying an alternative default order via the the ordering attribute, but you may face similar errors if ordering or filtering by other fields not known to the search backend.

That would be perfectly okay, but setting the ordering Meta attribute (or the ordering property of the ModelAdmin) does not help. I checked the stacktrace, and it seems that the IndexView adds the '-page_ptr' order field even if I specify another field to be the default:

ordering: ['date', 'date', '-page_ptr']

(The 'date' occurs twice here beause I set it in both the model's Meta and in the ModelAdmin subclass)