尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Wagtail 标签(Tagging)全攻略:从页面模型接入到自定义标签池与后台管理

Wagtail 标签(Tagging)全攻略:从页面模型接入到自定义标签池与后台管理 Wagtail 标签Tagging全攻略从页面模型接入到自定义标签池与后台管理【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail标签Tagging是 Wagtail 内容管理体系中高频使用的轻量级分类手段本篇技术指南以 docs/advanced_topics/tags.md 为核心脉络完整讲解在 Wagtail 中为页面与片段Snippet接入标签、按标签过滤查询、自定义独立标签池、关闭自由标签以及将标签注册为后台管理界面的完整方案。读完本文你将掌握ClusterTaggableManager、TaggedItemBase、TagBase的组合用法并理解后台标签控件在源码层面的实现原理与可调配置项。标签机制的底层架构两个 Django 模块的组合Wagtail 的标签能力并非自研而是由两个成熟的 Django 模块组合而成django-taggit提供通用化的标签实现包括Tag模型、TaggedItemBase/ItemBase中间模型以及TagBase基类负责标签数据与多对多关系的落库管理。django-modelcluster其核心贡献是ClusterTaggableManager——它扩展了 django-taggit 的TaggableManager允许标签关系先在内存中管理而不立即写入数据库。这一点对 Wagtail 至关重要因为预览preview与修订revision机制需要在不落库的情况下临时持有对象及其标签关系。两者分工清晰django-taggit 解决标签是什么、怎么存django-modelcluster 解决标签关系如何在暂存态与持久态之间流转。为页面模型添加标签步骤一定义 through 中间模型要给页面模型添加标签首先需要一个继承自TaggedItemBase的through 模型用来建立 django-taggit 的Tag模型与你的页面模型之间的多对多关系。through 模型通过ParentalKey指向宿主模型# models.py from modelcluster.fields import ParentalKey from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import TaggedItemBase class BlogPageTag(TaggedItemBase): content_object ParentalKey(demo.BlogPage, on_deletemodels.CASCADE, related_nametagged_items) class BlogPage(Page): ... tags ClusterTaggableManager(throughBlogPageTag, blankTrue) promote_panels Page.promote_panels [ ... FieldPanel(tags), ]关键点说明ParentalKey的related_nametagged_items会在BlogPage上暴露反向关联名供后续查询使用ClusterTaggableManager把整个多对多关系包装成一个直观的tags字段支持blankTrue允许留空将FieldPanel(tags)加入promote_panels后后台促进Promote标签页就会出现标签输入控件。在仓库的真实示例中可以看到完全一致的用法wagtail/test/testapp/models.py中的TaggedPageTag通过ParentalKey(tests.TaggedPage, related_nametagged_items, on_deletemodels.CASCADE)关联TaggedPage并在TaggedPage上声明tags ClusterTaggableManager(throughTaggedPageTag, blankTrue)见 wagtail/test/testapp/models.pywagtail/test/demosite/models.py的博客演示站点同样在BlogEntryPage上以throughBlogEntryPageTag的方式声明标签字段见 wagtail/test/demosite/models.py。Wagtail 后台为标签输入提供了良好的交互体验输入时带有typeahead 自动补全标签前缀联想并配有友好的标签图标。步骤二在视图与模板中使用标签关系多对多标签关系建立后即可在视图与模板中自由使用。典型场景是博客索引页接受?tag...查询参数按标签过滤文章列表from django.shortcuts import render class BlogIndexPage(Page): ... def get_context(self, request): context super().get_context(request) # Get blog entries blog_entries BlogPage.objects.child_of(self).live() # Filter by tag tag request.GET.get(tag) if tag: blog_entries blog_entries.filter(tags__nametag) context[blog_entries] blog_entries return context这里的blog_entries.filter(tags__nametag)会顺着BlogPage上的tags关联进行跨表过滤只保留标签名匹配的页面再把结果交给模板渲染。注意tags__name使用了 Django ORM 的跨关联查询语法双下划线。模板中展示页面标签并回链到过滤后的索引页{% for tag in page.tags.all %} a href{% pageurl page.blog_index %}?tag{{ tag }}{{ tag }}/a {% endfor %}{% for tag in page.tags.all %}遍历当前页面的全部标签链接地址复用索引页新增的?tag过滤能力实现点击标签 → 查看该标签下所有文章的闭环。除了tags__name这种正向查询Django 查询还可以利用 through 模型上的tagged_items反向关联名直接拿到与某个标签关联的BlogPage对象。为片段Snippet等非页面模型添加标签同样的配方可以用于非页面模型如片段。唯一要求是模型必须继承自modelcluster.models.ClusterableModel才能与ClusterTaggableManager兼容——因为只有 Clusterable 模型才具备内存中暂存子对象关系的能力。关于片段的完整概念可参考 Snippets 文档。仓库中wagtail/test/testapp/models.py的Address模型就是一个典型示例它同时继承ClusterableModel与Orderable以throughtests.AddressTag声明tags ClusterTaggableManager(throughtests.AddressTag, blankTrue)并通过ParentalKey挂在PersonPage之下见 wagtail/test/testapp/models.py说明标签字段可以灵活地出现在 Clusterable 子模型上。自定义标签模型为页面模型建立独立标签池上述方案中所有新建标签都会写入 django-taggit 的默认Tag模型这意味着它会被所有采用相同配方的模型共享——包括 Wagtail 的图片Image与文档Document模型。直接后果是标签字段的自动补全建议会混入其他模型曾使用过的标签。若希望某个页面模型拥有完全独立的标签池可以自定义标签模型与 through 模型from django.db import models from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey from taggit.models import TagBase, ItemBase class BlogTag(TagBase): class Meta: verbose_name blog tag verbose_name_plural blog tags class TaggedBlog(ItemBase): tag models.ForeignKey( BlogTag, related_nametagged_blogs, on_deletemodels.CASCADE ) content_object ParentalKey( todemo.BlogPage, on_deletemodels.CASCADE, related_nametagged_items ) class BlogPage(Page): ... tags ClusterTaggableManager(throughdemo.TaggedBlog, blankTrue)与默认方案的区别在于标签模型继承TagBase而非直接使用Tag定义了专属标签实体through 模型继承ItemBase并显式用ForeignKey指向自定义的BlogTagClusterTaggableManager的through指向demo.TaggedBlog。在后台标签字段会自动识别自定义标签模型其自动补全建议只取自该标签模型——这正是独立标签池的直观体现。关闭自由标签free_tagging False默认情况下标签字段是自由标签free tagging模式编辑者可以输入任意文本保存时未被识别的文本会自动创建为新标签。这在开放协作场景下很方便但也会造成标签库无限膨胀、出现拼写变体等垃圾标签。自定义标签模型可以通过free_tagging False关闭该行为只允许编辑者选择数据库中已存在的标签from taggit.models import TagBase from wagtail.snippets.models import register_snippet register_snippet class BlogTag(TagBase): free_tagging False class Meta: verbose_name blog tag verbose_name_plural blog tags这里同时用register_snippet把BlogTag注册为片段为管理员及拥有相应权限的用户提供维护允许使用的标签集合的后台界面。设置free_tagging False后编辑者无法再输入任意文本只能从自动补全下拉列表中选取既有标签。仓库对该机制有完整的实现与测试支撑表单层wagtail/admin/forms/tags.py中的TagField在clean()阶段会先做标签长度校验随后在free_tagging为 False 时把提交值过滤为仅保留 tag 模型中已存在的标签self.tag_model.objects.filter(name__invalue).values_list(name, flatTrue)见 wagtail/admin/forms/tags.py。控件层wagtail/admin/widgets/tags.py中的AdminTagWidget通过getattr(self.tag_model, free_tagging, True)读取标签模型设置并将其转换为前端选项autocompleteOnly: not free_tagging——free_taggingFalse时前端变为仅自动补全、禁止自由输入见 wagtail/admin/widgets/tags.py。测试模型wagtail/test/testapp/models.py中的RestaurantTag(TagBase)即声明了free_tagging False并通过TaggedRestaurant(ItemBase)与RestaurantPage关联用于验证该行为见 wagtail/test/testapp/models.py。将标签作为 Snippet 管理清理无用标签由于从内容中移除标签并不会删除Tag模型中的对应记录被移除的标签仍会出现在 typeahead 自动补全中。因此为项目提供一个标签管理后台是彻底清理无用标签的最佳途径。将Tag模型注册为片段即可在主菜单中获得标签管理界面支持增、改、删from wagtail.admin.panels import FieldPanel from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet from taggit.models import Tag class TagsSnippetViewSet(SnippetViewSet): panels [FieldPanel(name)] # only show the name field model Tag icon tag # change as required add_to_admin_menu True menu_label Tags menu_order 200 # will put in 3rd place (000 being 1st, 100 2nd) list_display [name, slug] search_fields (name,) register_snippet(TagsSnippetViewSet)将上述代码放入项目任意 app 的wagtail_hooks.py文件即可生效。参数说明panels [FieldPanel(name)]仅展示name字段。Tag模型的name与slug均为必填字段但slug 会在填写 name 时自动生成无需重复录入因此官方建议只显示 name 面板icon tag菜单图标可按需更换add_to_admin_menu True把标签管理加入后台主菜单menu_order 200菜单排序000 为第 1、100 为第 2200 即第 3 位list_display [name, slug]列表页展示列search_fields (name,)启用按名称搜索。后台标签控件源码解读与全局配置结合源码可以更深入地理解标签字段在后台的呈现方式。wagtail/admin/widgets/tags.py中的AdminTagWidget是 Wagtail 后台标签输入的核心控件它继承自 django-taggit 的TagWidget模板为wagtailadmin/widgets/tag_widget.html关键实现如下控件挂载了 Stimulus 控制器data-controllerw-tag并通过data-w-tag-url-value注入自动补全接口地址使用默认Tag模型时指向wagtailadmin_tag_autocomplete见 wagtail/admin/urls/init.py使用自定义标签模型时指向wagtailadmin_tag_model_autocomplete并按app_label与model_name路由见 wagtail/admin/urls/init.py。自动补全请求延迟固定为 200msdata-w-tag-delay-value 200。控件还读取两个全局设置项前端选项通过data-w-tag-options-value以 JSON 形式下发设置项默认值作用WAGTAIL_TAG_SPACES_ALLOWEDTrue是否允许标签包含空格。允许时多词标签会被自动包裹在双引号中禁止时标签只能是单词。该值同时决定帮助文本内容WAGTAIL_TAG_LIMITNone单个字段允许的最大标签数量限制为None时不限制实现见 wagtail/admin/widgets/tags.py对应测试见 wagtail/admin/tests/test_widgets.py。表单层的wagtail/admin/forms/tags.py还内置了标签长度校验validate_tag_length会以标签模型name字段的max_length默认取自TagBase._meta为上限超出长度的标签会触发ValidationError见 wagtail/admin/forms/tags.py。小结Wagtail 的标签体系由 django-taggit 与 django-modelcluster 协同支撑前者提供标签实体与多对多关系后者让标签关系可以在预览与修订场景下驻留内存。从页面模型到片段模型从默认共享标签池到TagBase/ItemBase自定义独立标签池再到free_tagging False收紧输入权限、将Tag注册为片段进行集中治理你可以按内容治理的严格程度自由组合这些能力。若需深入源码可继续阅读 wagtail/admin/widgets/tags.py 与 wagtail/admin/forms/tags.py并在 wagtail/test/testapp/models.py 中查看可直接运行的标签测试模型示例。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表