我正在尝试限制 wagtail 流字段块内特定 DocumentChooserBlock 的查询结果。
我已经知道您可以使用hooks将 DocumentChooser 的文件类型限制为页面类型,但我想避免在页面范围内限制可能的文件类型,以防其他 StreamField 块需要它们。
有没有可能的方法来实现我在这里想要实现的目标?
我正在尝试限制 wagtail 流字段块内特定 DocumentChooserBlock 的查询结果。
我已经知道您可以使用hooks将 DocumentChooser 的文件类型限制为页面类型,但我想避免在页面范围内限制可能的文件类型,以防其他 StreamField 块需要它们。
有没有可能的方法来实现我在这里想要实现的目标?
Usingwagtail-generic-chooser
提供了更多自定义选择器模式工作方式的能力。
wagtail-generic-chooser
pip install wagtail-generic-chooser
generic_chooser
到您项目的INSTALLED_APPS
.accept
通过创建一个扩展的自定义类来处理参数ModelChooserMixin
,这意味着在搜索时参数仍然会被传递。accept
URL 参数的处理以有条件地过滤返回的值。ModelChooserViewSet
它将处理Document
模式中列表的显示。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
但将用作 的基础,Block
也可用于FieldPanel
.AdminChooser
.__init__
方法中,我们提取了accept
kwarg,以便我们可以使用它来生成自定义 URL 参数。get_edit_item_url
允许单击所选文档以对其进行编辑的方法。to append the URL query param (note: I could not get
,而不会产生更多争论)。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
这里使用,而是使用钩子register_admin_viewset
并注册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
并包装了RestrictedDocumentChooser
已创建的小部件。__init__
同一个 kwargaccept
上被拉出并传递给RestrictedDocumentChooser
创建时。accept
。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)
Wagtail 的选择器模态系统与普通的 Django 小部件(用于呈现字段的 html 内容的类)有点不同,小部件本身主要呈现一个按钮“选择文档”,然后该按钮触发一个模态,它是一个单独的视图和模板.
正如您所指出的,construct_document_chooser_queryset
钩子可以限制这些模式中显示的结果,但只能访问正在查看的页面的请求对象,而不是用于触发该模式的 Widget。
有一种方法可以获得一些有限的所需功能,但它不适用于搜索结果,并且不会限制对该文件类型的任何其他上传。
DocumentChooserBlock
DocumentChooserBlock
并有一个自定义__init__
方法,该方法拉出一个 kwargaccept
并将其分配给小部件 attrs。attrs
的能力,这些都在渲染的 HTML 元素上输出,我们的自定义 Block 将我们想要的值分配给小部件,以便其他方法可以访问它。doc_block = SpecificDocumentChooserBlock(accept="svg,md") # uses accept kwarg
<input type="hidden" name="body-2-value" accept="svg,md" id="body-2-value" value="">
from wagtail.documents.blocks import DocumentChooserBlock
class SpecificDocumentChooserBlock(DocumentChooserBlock):
"""
Existing DocumentChooserBlock with the ability to add widget attrs based on the
accept kwarg, anything on self.widget.attrs will be added to the hidden
input field (so be careful what key is used).
"""
def __init__(self, accept=None, **kwargs):
super().__init__(**kwargs)
self.widget.attrs["accept"] = accept
data-chooser-url
上的。document-chooser
wagtail_hooks.py
,因为无论如何我们都需要该文件,而且我们知道它只会在运行时运行一次。widget.render_html(
是关键部分,我们正在使用**
语法来解压缩任何 widget.attrs 值(一个将是accept
我们自定义块设置的项目)。from wagtail.core.telepath import register as telepath_register
from wagtail.documents.widgets import AdminDocumentChooser, DocumentChooserAdapter
class CustomDocumentChooserAdapter(DocumentChooserAdapter):
def js_args(self, widget):
return [
widget.render_html(
# this line is changed, allocate any widget.attrs to the attrs passed to render_html
"__NAME__",
None,
attrs={**widget.attrs, "id": "__ID__"},
),
widget.id_for_label("__ID__"),
]
telepath_register(CustomDocumentChooserAdapter(), AdminDocumentChooser)
INSTALLED_APPS
为此步骤添加更多应用程序。myapp/templates/wagtaildocs/widgets/document_chooser.html
,后面的部分在templates
这里很关键,因为我们要覆盖和扩展这个确切的模板。chooser_attributes
,因为这是添加data-chooser-url
选择器模态触发器使用的块。data-chooser-url
添加到 URL 的查询字符串<div id="body-2-value-chooser" class="chooser document-chooser blank" data-chooser-url="/admin/documents/chooser/?accept=svg,md">
{% extends "wagtaildocs/widgets/document_chooser.html" %}
{% comment %}
This template overrides the Wagtail default chooser field, this is not the modal but
the button / selected value shown in the page editor.
chooser_attributes are the attributes that are used by the modal trigger, we will
override the 'data-chooser-url' value with a url param
{% endcomment %}
{% block chooser_attributes %}data-chooser-url="{% url "wagtaildocs:chooser" %}{% if attrs.accept %}?accept={{ attrs.accept }}{% endif %}"{% endblock %}
accept
查询字符串参数construct_document_chooser_queryset
现在,可以拉入 GET 参数accept
并对其进行解析以生成一组不同的文档结果。@hooks.register("construct_document_chooser_queryset")
def show_accepted_documents_only(documents, request):
accept = request.GET.get("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
documents = documents.filter(query)
return documents
CustomDocumentChooserAdapter
因此请务必密切关注 Wagtail 代码更改。