I'm trying to limit query results for a specific DocumentChooserBlock inside of a wagtail stream field block.
I already know that you can limit file types for DocumentChooser for a page type by using hooks, but I would like to avoid limiting possible file types page wide in case I need them for other StreamField blocks.
Are there any possible ways to implement what I am trying to achieve here?
Using wagtail-generic-chooser
offers much more ability to customise the way the Chooser modal works.
wagtail-generic-chooser
pip install wagtail-generic-chooser
generic_chooser
to your project's INSTALLED_APPS
.accept
parameter by creating a custom class that extends the ModelChooserMixin
, this will mean the param still gets passed when searching.accept
URL param to conditionally filter the returned values.ModelChooserViewSet
which will handle the showing of the Document
listing within the modal.from django.db.models import Q
from generic_chooser.views import ModelChooserMixin, ModelChooserViewSet
from wagtail.documents.models import Document
class RestrictedDocumentChooserMixin(ModelChooserMixin):
# preserve this URL parameter on pagination / search
preserve_url_parameters = [
"accept",
]
def get_unfiltered_object_list(self):
objects = super().get_unfiltered_object_list()
accept = self.request.GET.get("accept")
print("get_unfiltered_object_list", accept)
if accept:
accepted_files = accept.split(",")
queries = [Q(file__iendswith=f".{value}") for value in accepted_files]
query = queries.pop()
for item in queries:
query |= item
objects = objects.filter(query)
return objects
class RestrictedDocumentChooserViewSet(ModelChooserViewSet):
chooser_mixin_class = RestrictedDocumentChooserMixin
icon = "doc"
model = Document
page_title = "Choose a document"
per_page = 10
order_by = "title"
fields = ["title", "file"]
Block
but will be used as the base for the Block
and can also be used for a FieldPanel
.AdminChooser
.__init__
method we pull out the accept
kwarg so we can use it to generate the custom URL param.get_edit_item_url
method which will allow the clicking of a selected Document to edit it.to append the URL query param (note: I could not get
reverse` working here without heaps more wrangling).from django.contrib.admin.utils import quote
from django.urls import reverse
from generic_chooser.widgets import AdminChooser
from wagtail.documents.models import Document
class RestrictedDocumentChooser(AdminChooser):
def __init__(self, **kwargs):
self.accept = kwargs.pop("accept")
super().__init__(**kwargs)
choose_one_text = "Choose a Document"
choose_another_text = "Choose another document"
link_to_chosen_text = "Edit this document"
model = Document
choose_modal_url_name = "restricted_document_chooser:choose"
def get_choose_modal_url(self):
url = super().get_choose_modal_url()
return url + "?accept=%s" % self.accept
def get_edit_item_url(self, item):
return reverse("wagtaildocs:edit", args=[item.id])
construct_document_chooser_queryset
here, instead use the hook register_admin_viewset
and register the RestrictedDocumentChooserViewSet
.from wagtail.core import hooks
from .views import RestrictedDocumentChooserViewSet
# ... other hooks etc
@hooks.register("register_admin_viewset")
def register_restricted_document_chooser_viewset():
return RestrictedDocumentChooserViewSet(
"restricted_document_chooser", url_prefix="restricted-document-chooser"
)
Block
ChooserBlock
and wraps the RestrictedDocumentChooser
widget that has been created.__init__
the same kwarg accept
is pulled out and passed to the RestrictedDocumentChooser
when created.accept
though. doc_block = RestrictedDocumentChooserBlock(accept="svg,md")
from django.utils.functional import cached_property
from wagtail.images.blocks import ChooserBlock
# ...
class RestrictedDocumentChooserBlock(ChooserBlock):
def __init__(self, **kwargs):
self.accept = kwargs.pop("accept")
super().__init__(**kwargs)
@cached_property
def target_model(self):
from wagtail.documents.models import Document
return Document
@cached_property
def widget(self):
from .widgets import RestrictedDocumentChooser
return RestrictedDocumentChooser(accept=self.accept)
def get_form_state(self, value):
return self.widget.get_value_data(value)