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

资讯详情

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

Wagtail 2.12.3 补丁版源码解析:别名页复制、语言切换与翻译复制四大 Bug 修复

Wagtail 2.12.3 补丁版源码解析:别名页复制、语言切换与翻译复制四大 Bug 修复 Wagtail 2.12.3 补丁版源码解析别名页复制、语言切换与翻译复制四大 Bug 修复【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail本文以 Wagtail 2.12.32021 年 3 月 5 日发布的官方发布说明为骨架逐条拆解该补丁版本修复的四个关键问题——django-treebeard 迁移冲突、复制别名页面崩溃、修改LANGUAGE_CODE后编辑页面报错、copy_for_translation对模型继承与ClusterableModel处理不当——并结合当前仓库中的动作层、模型层与测试代码还原每条修复背后的调用链与设计意图。读完本文你将理解 Wagtail 页面复制/别名/翻译机制的核心实现以及如何在自己的项目中规避同类坑。2.12.3 补丁版概览Wagtail 2.12.3 是一个纯 Bug 修复版本发布于 2021 年 3 月 5 日。官方发布说明docs/releases/2.12.3.rst列出的修复清单共四条Un-pin django-treebeard following upstream fix for migration issueMatt Westcott解除对 django-treebeard 的版本固定上游已修复迁移问题Prevent crash when copying an alias pageKarl Hobley修复复制别名alias页面时的崩溃Prevent errors on page editing after changing LANGUAGE_CODEMatt Westcott修复修改LANGUAGE_CODE后编辑页面报错Correctly handle model inheritance andClusterableModeloncopy_for_translationKarl Hobley修复copy_for_translation对模型继承与ClusterableModel的处理。这四条修复全部集中在 Wagtail 的多语言i18n与页面复制体系上涉及wagtail.actions动作层、wagtail.models.copying复制底层、wagtail.models.i18n可翻译模型三大模块。下文逐一深入。修复一解除 django-treebeard 版本固定消除迁移冲突问题背景django-treebeard是 Wagtail 页面树基于 MPTT 思想的树形结构的底层依赖Wagtail 通过它为Page提供path、depth、numchild等树形字段与add_child、add_sibling等操作。在 2.12.3 之前Wagtail 曾因 treebeard 某个版本的迁移migration与 Wagtail 自身迁移产生冲突而将其版本钉死pin在特定版本上游修复该迁移问题后Wagtail 顺势解除固定恢复宽松的版本约束。当前仓库中的依赖约束在当前仓库的 pyproject.toml 中django-treebeard的约束为django-treebeard4.8,6.0,采用「最低版本 大版本上限」的区间约束而非单一固定版本正是这次「un-pin」的延续结果——既保证拿到上游的迁移修复又避免未来大版本升级带来的兼容性风险。如果你的项目升级到包含该修复的 Wagtail 版本时遇到 treebeard 相关迁移报错应优先执行makemigrations/migrate让两个包按新约束对齐而不是继续手工钉版本。修复二复制别名页面不再崩溃什么是 Alias 页面别名alias页面是 Wagtail 多站点/多语言场景的重要概念。从 wagtail/actions/create_alias.py 的类文档可以看到它的语义An alias is like a copy, but an alias remains in sync with the original page. They are not directly editable and do not have revisions.即alias 与普通副本不同它会与原始页面保持同步、不可直接编辑、没有修订记录。实现上CreatePageAliasAction通过_copy复制页面时写入了alias_of字段、将draft_title与title对齐、并显式把latest_revision排除在复制字段之外create_alias.pyexclude_fields [ id, path, depth, numchild, url_path, index_entries, postgres_index_entries, latest_revision, # for page aliases do not have revisions ]崩溃原因与修复验证2.12.3 之前若用户对某个 alias 页面再次执行copy()复制流程会因为 alias 缺少修订等「非普通页面」属性而崩溃。修复后复制 alias 页面应产出一个普通页面副本而非新的 alias。仓库中的回归测试 wagtail/tests/test_page_model.py 精确验证了这一行为def test_copy_alias_page(self): about_us SimplePage.objects.get(url_path/home/about-us/) about_us_alias about_us.create_alias(update_slugabout-us-alias) about_us_alias_copy about_us_alias.copy( update_attrs{slug: about-us-alias-copy} ) self.assertIsInstance(about_us_alias_copy, SimplePage) self.assertEqual(about_us_alias_copy.slug, about-us-alias-copy) ... # The copy should just be a copy of the original page, not an alias self.assertIsNone(about_us_alias_copy.alias_of)关键断言是assertIsNone(about_us_alias_copy.alias_of)从 alias 复制出的副本是普通页面alias_of为空与原始页面不再保持同步。这套「alias 可复制、副本独立」的语义由动作层与copy底层共同保证也是copy_for_translation将缺失父页面复制为 alias见修复四的基础。修复三修改 LANGUAGE_CODE 后编辑页面不再报错问题背景Wagtail 的多语言模型TranslatableMixin依赖Locale表记录站点内容语言。当站点以某种LANGUAGE_CODE初始化页面树后若后续直接修改 Django 设置中的LANGUAGE_CODE而数据库中的Locale记录未同步变更编辑页面就会报错——因为 Wagtail 会按LANGUAGE_CODE推断默认 locale若与数据库内容语言的language_code不一致即触发异常。修复后的行为与回归测试当前仓库在 wagtail/admin/tests/pages/test_edit_page.py 中用两条测试覆盖该场景模拟「页面树以德语创建但配置未变更」的脏状态def test_edit_after_change_language_code(self): Verify that changing LANGUAGE_CODE with no corresponding database change does not break editing # Add a draft revision self.child_page.title Hello world updated self.child_page.save_revision() # Hack the Locale model to simulate a page tree that was created with LANGUAGE_CODE de Locale.objects.update(language_codede) # Tests that the edit page loads response self.client.get( reverse(wagtailadmin_pages:edit, args(self.child_page.id,)) ) self.assertEqual(response.status_code, 200) ...测试覆盖了「有草稿修订」与「无任何修订」两种变体第二条test_edit_after_change_language_code_without_revisions在 test_edit_page.py。修复的关键在于页面编辑流程不再把「当前LANGUAGE_CODE对应的 locale」作为页面 locale 的唯一判据而是允许页面自身的Locale.language_code与当前配置不一致时仍能正常加载与提交。从 wagtail/models/i18n.py 的Locale模型可以看到Locale.language_code_is_valid()会检查language_code是否属于WAGTAIL_CONTENT_LANGUAGES配置的合法语言集合2.12.3 的修复正是让编辑视图在这类「历史遗留语言」存在时优雅降级而不是抛出异常。如果你在自建站点中改动了LANGUAGE_CODE注意同步维护Locale表与WAGTAIL_CONTENT_LANGUAGES即可完全规避该问题。修复四copy_for_translation 正确处理模型继承与 ClusterableModel这是 2.12.3 中最具技术深度的一条修复涉及 Wagtail 翻译复制机制的底层。我们先看修复后的实现再对照测试理解其行为。动作层两条翻译复制路径翻译复制在动作层拆分为两个类wagtail/actions/copy_for_translation.pyCopyPageForTranslationAction面向Page目标语言页面作为已翻译父页面的子页面创建父页面缺失时抛ParentNotTranslatedError或通过copy_parentsTrue递归补齐CopyForTranslationAction面向任意可翻译对象snippet 等非页面模型由TranslatableMixin.copy_for_translation触发wagtail/models/i18n.py。两者的公共入口都是_copy()底层函数wagtail/models/copying.pydef _copy(source, exclude_fieldsNone, update_attrsNone): data_dict _extract_field_data(source, exclude_fieldsexclude_fields) target source.__class__(**data_dict) if update_attrs: for field, value in update_attrs.items(): if field not in data_dict: continue setattr(target, field, value) if isinstance(source, ClusterableModel): child_object_map source.copy_all_child_relations( target, excludeexclude_fields ) else: child_object_map {} return target, child_object_map模型继承get_translation_model 是关键TranslatableMixinwagtail/models/i18n.py通过translation_keyUUIDlocale外键的联合唯一约束标识一组翻译对象。在多表继承multi-table inheritance下Page正是典型locale/translation_key字段定义在父表而get_translation_model()wagtail/models/i18n.py负责定位真正持有这两个字段的模型The Translation model is the model that has thelocaleandtranslation_keyfields. Typically this would be the current model, but it may be a super-class if multi-table inheritance is in use (as is the case forwagtailcore.Page).2.12.3 之前copy_for_translation在处理继承模型时可能错误地在新副本上设置/读取翻译字段导致locale与translation_key落错表。修复后CopyForTranslationAction._copy_for_translation先通过get_latest_revision_as_object()取最新内容含草稿再叠加三类排除字段后执行复制exclude_fields ( getattr(object, default_exclude_fields_in_copy, []) getattr(object, exclude_fields_in_copy, []) (exclude_fields or []) ) translated, child_object_map _copy(object, exclude_fieldsexclude_fields) translated.locale localeClusterableModel可翻译子对象递归更新 localeClusterableModel是 Wagtail 处理「页面附带子对象集合」如 EventPage 的 speakers 列表的机制。复制时_copy()通过copy_all_child_relations把全部子关系复制到新对象并返回child_object_map。修复的核心在于子对象中凡是TranslatableMixin实例都必须递归更新其locale同时保持translation_key与原对象一致这样get_translations()才能跨语言关联。页面路径的实现是process_child_object回调wagtail/actions/copy_for_translation.pydef process_child_object(original_page, page_copy, child_relation, child_object): from wagtail.models import TranslatableMixin if isinstance(child_object, TranslatableMixin): child_object.locale locale return page.copy( totranslated_parent, update_attrs{locale: locale, slug: slug}, copy_revisionsFalse, keep_liveFalse, reset_translation_keyFalse, process_child_objectprocess_child_object, exclude_fieldsexclude_fields, log_actionwagtail.copy_for_translation, )注意reset_translation_keyFalse翻译副本保留原始translation_key这正是「翻译」与「复制」的分水岭普通复制会生成新translation_key。非页面模型走同样的逻辑copy_for_translation.py遍历child_object_map更新可翻译子对象的 locale。测试实证四条路径全部有回归覆盖继承模型翻译wagtail/tests/test_translatablemixin.py 中test_get_translation_model断言继承自TestModel的InheritedTestModel其翻译模型仍是父类TestModeltest_copy_inherited_model_for_translation验证继承模型副本的translation_key与locale正确。ClusterableModel 翻译同一文件test_copy_clusterable_model_for_translationtest_translatablemixin.py创建含「非可翻译子对象 可翻译子对象」的ClusterableTestModel断言复制后子对象被复制、可翻译子对象的translation_key保持不变、locale更新为目标语言。页面级翻译wagtail/tests/test_page_model.py 的TestCopyForTranslation覆盖了首页复制slug 追加语言代码home-fr、副本为草稿态、日志动作wagtail.copy_for_translation、slug 冲突自动加后缀home-fr-1、父页面缺失抛ParentNotTranslatedError、copy_parentsTrue时父页面以alias形式补齐fr_eventindex.alias_of指向英文原页面、以及可翻译子对象speaker的 locale/translation_key 联动。其中「缺失父页面自动创建为 alias」的设计copy_for_translation.py非常值得注意当父页面未翻译时若copy_parentsTrue父页面会被copy_for_translation(locale, copy_parentsTrue, aliasTrue)复制成alias而非普通副本因为父页面只是占位载体、不应产生独立编辑的副本这与修复二「alias 不可直接编辑、与源同步」的语义完全自洽——两条修复在架构上是咬合的。升级与验证建议依赖对齐升级后确认django-treebeard满足4.8,6.0区间约束并重新执行数据库迁移回归验证若你的站点使用了 alias 页面或多语言功能可参照上述测试用例在 shell 中手动验证page.copy_for_translation(locale)、page.create_alias(...)、对 alias 执行copy()、以及模拟Locale与LANGUAGE_CODE不一致时的页面编辑数据一致性若历史上曾手工修改LANGUAGE_CODE建议检查wagtailcore_locale表中记录的language_code是否与WAGTAIL_CONTENT_LANGUAGES配置一致必要时通过Locale管理界面修正避免再触发修复三所覆盖的编辑异常。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表