我正在重构我的Wagtail应用程序以删除一个只有一个项目的IndexPage,并将该项目移动为当前IndexPage父项的子项。
基本上离开了这个:
Page--|
|--IndexPage--|
|--ChildPages
(这里只有1个)
到此:
Page--|
|--ChildPage
我对模型进行了更改,以便此结构用于创建新内容并修复相关视图以直接指向ChildPage。但现在我想将当前数据迁移到新结构,我不知道如何去做...理想情况下,这将在迁移中完成,这样我们就不必手动执行任何操作。
有没有办法在迁移过程中以编程方式将这些ChildPage向上移动?
答案 0 :(得分:5)
不幸的是,存在一个严格的限制(可能)排除了在迁移中进行页面树调整的可能性:插入,移动和删除页面等树操作在Page
模型上实现为方法,并且在迁移您只能访问该模型的“虚拟”版本,它只允许您访问数据库字段和基本ORM方法,而不是那些自定义方法。
(您可以通过在迁移中使用from wagtail.wagtailcore.models import Page
并使用标准Page = apps.get_model("wagtailcore", "Page")
方法而不是标准Page
方法来解决此问题,但我不建议这样做 - 如果迁移是在迁移序列中的某个点运行的,Page
模型仍在构建,并且与模型的“真实”状态不匹配。)
相反,我建议编写一个Django management command来进行树操作 - 在管理命令中, 可以安全地从wagtailcore导入Page
模型,以及作为您的特定页面模型。 move(target, pos)
提供了from myapp.models import IndexPage
# ...
for index_page in IndexPage.objects.all():
for child_page in index_page.get_children():
child_page.move(index_page, 'right')
index_page.delete()
方法as per the Treebeard API - 移动子页面的代码可能如下所示:
{{1}}
答案 1 :(得分:0)
从理论上讲,应该可以使用Daniele Miele在Django-treebeard and Wagtail page creation中演示的相同操作来构建move()。看起来像这样的Python伪代码:
def move(page, target):
# assuming pos='last_child' but other cases follow similarly,
# just with more bookkeeping
# first, cut it out of its old tree
page.parent.numchild -= 1
for sib in page.right_siblings: # i.e. those with a greater path
old = sib.path
new = sib.path[:-4] + (int(sib.path[-4:])-1):04
sib.path = new
for nib in sib.descendants:
nib.path = nib.path.replace_prefix(old, new)
# now, update itself
old_path = page.path
new_path = target.path + (target.numchild+1):04
page.path = new_path
old_url_path = page.url_path
new_url_path = target.url_path + page.url_path.last
page.url_path = new_url_path
old_depth = page.depth
new_depth = target.depth + 1
page.depth = new_depth
# and its descendants
depth_change = new_depth - old_depth
for descendant in page.descendants:
descendant.path = descendant.path.replace_prefix(old_path, new_path)
descendant.url_path = descendant.url_path.replace_prefix(old_path, new_path)
descendant.depth += depth_change
# finally, update its new parent
target.numchild += 1
使这种操作比看起来简单的核心概念是:当节点重新排序或移动时,所有后代都需要更新,但是仅 更新他们需要的是他们祖先得到的完全相同的更新。它被用作前缀替换(如果是str)或差异(如果是int),这两个都不需要了解有关后代的确切值。
那是说,我还没有测试过;它很复杂,很容易弄乱;而且没有办法知道我是否更新了Wagtail关心的每个不变式。因此,对于管理命令方式也有话要说。