Reputation: 1832
I have two models, one of which is an index page for the other:
class FooIndexPage(Page):
category = models.IntegerField(unique=True)
subpage_types = ['foo.fooPage']
class FooPage(Page):
category = models.IntegerField()
I obviously left out most of the fields because they are irrelevant to my question. The category
field of FooPage
is actually derived from other fields; I just put it that way for the sake of simplicity.
What I want to achieve: Each FooPage
should eventually be a child of a certain FooIndexPage
in the tree structure. However, before the page is created, it is not yet clear which page is exactly the appropriate FooIndexPage
(since the category
is derived from the other fields, as mentioned above). There is also the possibility that it does not yet exist and must be created first. The whole thing should happen automatically when a user creates a FooPage
(the user creates the page in the admin interface as a child of the home page). As soon as the user saves the page, it should be moved to the corresponding FooIndexPage
, which may have to be created first.
I have attempted to solve it by overriding the save
method of FooPage
and moving the page accordingly after creation:
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
category = self.category
if not FooIndexPage.objects.filter(category=category).exists():
self.get_parent().add_child(instance=FooIndexPage(category=category))
index_page = FooIndexPage.objects.get(category=category)
self.move(index_page, 'last-child')
The correct FooIndexPage
is created if it does not exist. However, the code raises an exception on self.move
:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/.../lib/python3.8/site-packages/wagtail/core/models.py", line 1381, in move
super().move(target, pos=pos)
File "/home/.../lib/python3.8/site-packages/treebeard/mp_tree.py", line 1095, in move
return MP_MoveHandler(self, target, pos).process()
File "/home/.../lib/python3.8/site-packages/treebeard/mp_tree.py", line 472, in process
if self.target.is_descendant_of(self.node):
AttributeError: 'NoneType' object has no attribute 'is_descendant_of'
This seems to be a known issue of treebeard, which occurs when the parent page has no children yet. I was not able to find a solution, though.
Is there a way to overwrite the parent page when creating a page?
Upvotes: 1
Views: 887
Reputation: 25227
As per the Treebeard docs:
django-treebeard uses Django raw SQL queries for some write operations, and raw queries don’t update the objects in the ORM since it’s being bypassed.
Because of this, if you have a node in memory and plan to use it after a tree modification (adding/removing/moving nodes), you need to reload it.
By creating index_page
, you're making a modification to the tree while the node self
is still in memory. It's hard to know what kind of issues this might cause, but if for example creating the index page caused self
's path
value to change, then the move
operation might perform updates based on the stale path
value. It's probably worth running ./manage.py fixtree
at this point to check whether any corruption to the tree structure has occurred.
Running self.refresh_from_db()
before the move should avoid that, but I'm not 100% confident that's safe to do inside a save
method, so I'd be inclined to avoid that by fetching a new copy of the self
node and running move
on that instead:
new_self = Page.objects.get(pk=self.pk)
new_self.move(index_page, 'last-child')
Upvotes: 1