/
rebuild_page_tree.py
131 lines (113 loc) · 5.29 KB
/
rebuild_page_tree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from django.core.management.base import NoArgsCommand
from django.utils.encoding import smart_text
from optparse import make_option
from fluent_pages import appsettings
from fluent_pages.extensions import page_type_pool
from fluent_pages.models.db import UrlNode_Translation, UrlNode
class Command(NoArgsCommand):
"""
Update the tree, rebuild the translated URL nodes.
"""
help = "Update the cached_url for the translated URL node tree"
option_list = (
make_option(
'-p', '--dry-run', action='store_true', dest='dry-run', default=False,
help="Only list what will change, don't make the actual changes."
),
make_option(
'-m', '--mptt-only', action='store_true', dest='mptt-only', default=False,
help="Only fix the MPTT fields, leave URLs unchanged."
),
) + NoArgsCommand.option_list
def handle_noargs(self, **options):
is_dry_run = options.get('dry-run', False)
mptt_only = options.get('mptt-only', False)
slugs = {}
overrides = {}
parents = dict(UrlNode.objects.values_list('id', 'parent_id'))
self.stdout.write("Updated MPTT columns")
if is_dry_run and mptt_only:
# Can't really do anything
return
if not is_dry_run:
# Fix MPTT first, that is the basis for walking through all nodes.
UrlNode.objects.rebuild()
self.stdout.write("Updated MPTT columns")
if mptt_only:
return
self.stdout.write("Updating cached URLs")
self.stdout.write("Page tree nodes:\n\n")
type_len = str(max(len(plugin.type_name) for plugin in page_type_pool.get_plugins()))
col_style = u"| {0:6} | {1:6} | {2:" + type_len + "} | {3:6} | {4}"
header = col_style.format("Site", "Page", "Type", "Locale", "URL")
sep = '-' * (len(header) + 40)
self.stdout.write(sep)
self.stdout.write(header)
self.stdout.write(sep)
# In a single query, walk through all objects in a logical order,
# which traverses through the tree from parent to children.
translations = (UrlNode_Translation.objects
.select_related('master')
.order_by('master__parent_site__id', 'master__tree_id', 'master__lft', 'language_code')
)
for translation in translations:
slugs.setdefault(translation.language_code, {})[translation.master_id] = translation.slug
overrides.setdefault(translation.language_code, {})[translation.master_id] = translation.override_url
page = translation.master
if page.parent_id:
if page.parent_id not in slugs[translation.language_code]:
self.stderr.write("WARNING: Parent #{0} is not translated in '{1}', while the child #{2} is.".format(
page.parent_id, translation.language_code, translation.master_id
))
old_url = translation._cached_url
try:
new_url = self._construct_url(translation.language_code, translation.master_id, parents, slugs, overrides)
except KeyError:
if is_dry_run:
# When the mptt tree is broken, some URLs can't be correctly generated yet.
self.stderr.write("Failed to determine new URL for {0}, please run with --mptt-only first.".format(old_url))
return
raise
if old_url != new_url:
translation._cached_url = new_url
if not is_dry_run:
translation.save()
if old_url != new_url:
self.stdout.write(smart_text(u"{0} {1} {2}\n".format(
col_style.format(
page.parent_site_id, page.pk, page.plugin.type_name,
translation.language_code, translation._cached_url
),
"WILL CHANGE from" if is_dry_run else "UPDATED from",
old_url
)))
else:
self.stdout.write(smart_text(col_style.format(
page.parent_site_id, page.pk, page.plugin.type_name,
translation.language_code, translation._cached_url
)))
def _construct_url(self, language_code, child_id, parents, slugs, overrides):
active_choices = appsettings.FLUENT_PAGES_LANGUAGES.get_active_choices(language_code)
breadcrumb = []
cur = child_id
while cur is not None:
breadcrumb.insert(0, cur)
cur = parents[cur]
url_parts = ['']
for id in breadcrumb:
try:
# Resets url_parts
override = overrides[language_code][id]
if override:
url_parts = [override]
continue
except KeyError:
pass
# Add first one found, preferably the normal language, fallback otherwise.
for lang in active_choices:
try:
url_parts.append(slugs[lang][id])
break
except KeyError:
continue
return (u'/'.join(url_parts) + u'/').replace('//', '/')