<p>Wagtail的选择器模式系统的工作原理与普通Django小部件(用于呈现字段html内容的类)略有不同,小部件本身主要呈现一个按钮“选择文档”,然后按钮触发一个模式,该模式是一个单独的视图和模板</p>
<p>正如您所指出的,<a href="https://docs.wagtail.io/en/stable/reference/hooks.html#construct-document-chooser-queryset" rel="nofollow noreferrer">^{<cd1>} hook</a>可以限制这些模式中显示的结果,但只能访问正在查看的页面的请求对象,而不能访问用于触发该模式的小部件</p>
<p>有一种方法可以获得一些有限的所需功能,但它不适用于搜索结果,也不会将任何附加上载限制为该文件类型</p>
<h2>步骤1-创建自定义<code>DocumentChooserBlock</code></h2>
<ul>
<li>这个block类扩展了<code>DocumentChooserBlock</code>,并有一个自定义的<code>__init__</code>方法,该方法提取kwarg<code>accept</code>并将其分配给小部件属性</李>
<li>Django小部件都有<a href="https://docs.djangoproject.com/en/3.2/ref/forms/widgets/#django.forms.Widget.attrs" rel="nofollow noreferrer">accept ^{<cd6>}</a>的功能,这些都是在呈现的HTML元素上输出的,我们的自定义块将我们想要的值分配给小部件,以便其他方法可以访问它</李>
<li>此块的使用方式与任何其他块相同,但将使用accept <code>doc_block = SpecificDocumentChooserBlock(accept="svg,md") # uses accept kwarg</code>的kwarg</li>
<li>您可以通过查看DOM(浏览器中的inspect元素)来确认这一点,在“选择文档”之后,将有一个类似<code><input type="hidden" name="body-2-value" accept="svg,md" id="body-2-value" value=""></code>的隐藏属性</li>
</ul>
<h4><strong>块.py</strong><h4>
<pre class="lang-py prettyprint-override"><code>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
</code></pre>
<h2>步骤2-确保小部件属性被传递到模式触发器</h2>
<ul>
<li>不幸的是,用于查询URL的数据不在上面的HTML输入元素上,而是在容器div上,请参见<code>document-chooser</code>块div上的<code>data-chooser-url</code></li>
<li>此数据属性由名为<a href="https://github.com/wagtail/telepath" rel="nofollow noreferrer">Telepath</a>的系统生成</李>
<li>需要理解的主要部分是,有一个类用于告诉浏览器基于小部件渲染什么,默认情况下,它不会传递小部件属性</李>
<li>下面的代码应该添加到<code>wagtail_hooks.py</code>,因为我们无论如何都需要该文件,而且我们知道它在运行时只运行一次</李>
<li>行<code>widget.render_html(</code>是关键部分,我们正在使用<code>**</code>语法来解压任何widget.attrs值(其中一个是自定义块设置的<code>accept</code>项)</李>
</ul>
<h4><strong>hooks.py</strong><h4>
<pre><code>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)
</code></pre>
<h2>步骤3-覆盖文档选择器的管理模板</h2>
<ul>
<li>请查看<a href="https://docs.wagtail.io/en/stable/advanced_topics/customisation/admin_templates.html#customising-admin-templates" rel="nofollow noreferrer">Customising admin templates</a>的文档,因为您可能需要为<code>INSTALLED_APPS</code>添加更多的应用程序来完成此步骤</李>
<li>创建一个新文件<code>myapp/templates/wagtaildocs/widgets/document_chooser.html</code>,<code>templates</code>之后的部分在这里非常重要,因为我们想要覆盖和扩展这个精确的模板</李>
<li>在模板中,我们将扩展原始块并覆盖块<code>chooser_attributes</code>,因为这是添加选择器模式触发器使用的<code>data-chooser-url</code>的部分</李>
<li>重要提示:在这里重新启动dev服务器,因为您已经添加了一个新的模板覆盖</李>
<li>完成后,在浏览器中检查包含“选择文档”按钮的元素,您应该能够看到容器元素现在有一个<code>data-chooser-url</code>,URL中添加了一个查询字符串<code><div id="body-2-value-chooser" class="chooser document-chooser blank" data-chooser-url="/admin/documents/chooser/?accept=svg,md"></code></li>
</ul>
<h4><strong>myapp/templates/wagtaildocs/widgets/document\u chooser.html</strong><h4>
<pre><code>{% 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 %}
</code></pre>
<h2>步骤4-处理<code>accept</code>查询字符串参数</h2>
<ul>
<li>现在使用<code>construct_document_chooser_queryset</code>,可以拉入GET参数<code>accept</code>并对其进行解析以生成一组不同的文档结果</李>
</ul>
<h4><strong>摇尾钩.py</strong><h4>
<pre class="lang-py prettyprint-override"><code>@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
</code></pre>
<h2>警告</h2>
<ul>
<li>此解决方案不会阻止用户仅上传模式中的特定文件,但您可以探索使用一些CSS隐藏该选项卡(块接受类名属性)</李>
<li>当用户在模式中搜索时,不幸的是,它不会遵守以这种方式设置的URL</李>
<li>解决方案在各种版本中都可能很脆弱,尤其是<code>CustomDocumentChooserAdapter</code>,所以一定要注意摇摆代码的变化</李>
</ul>