在迁移过程中移动Wagtail页面

时间:2017-10-05 20:21:15

标签: django-migrations wagtail

我正在重构我的Wagtail应用程序以删除一个只有一个项目的IndexPage,并将该项目移动为当前IndexPage父项的子项。

基本上离开了这个:

Page--| |--IndexPage--| |--ChildPages(这里只有1个)

到此:

Page--| |--ChildPage

我对模型进行了更改,以便此结构用于创建新内容并修复相关视图以直接指向ChildPage。但现在我想将当前数据迁移到新结构,我不知道如何去做...理想情况下,这将在迁移中完成,这样我们就不必手动执行任何操作。

有没有办法在迁移过程中以编程方式将这些ChildPage向上移动?

2 个答案:

答案 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关心的每个不变式。因此,对于管理命令方式也有话要说。