DEV Community

Cover image for Unchaining Django in Wagtail
Chiemezuo
Chiemezuo

Posted on

Unchaining Django in Wagtail

Introduction

This blog post is about a code snippet I'm proud of. I was trying to solve an issue that took me quite some time. The fix took 2 lines of code, but required a full understanding of the Django framework.

The problem

computer bug image
In Wagtail, creating pages from the dashboard works well, until a certain point. If you were migrating content from an old Blog, with hundreds of pages, the strain of recreating all the pages and then copy-pasting would outweigh the benefits of migrating. The better way to do this would be with a script. You export content from the old system into a CSV file and then read it into the Page model and save each row as an instance. All from the Django shell.
The particular problem: After migration, downloading the XLSX file from the dashboard would crash with an AttributeError, despite your changes being reflected.

My thought process

thought process image

Reproducing the error

To find out why it crashed, I had to first reproduce the error. I mimicked a script loading pages directly into the database from the shell. So, using the Django shell, I imported Wagtail's Page model, and the User model, so I could attach an Author to the page. Afterward, I looked through wagtail/models/__init__.py to know the required kwargs for a Page instance. I created one, saved it with save(), and published it with publish(), passing in the revision arg. I refreshed, and the change was reflected in the dashboard. On the Aging pages interface, I saw pages created via the dashboard, and the page created via the shell. On clicking the button for downloading the XLSX report, the dashboard crashed. I successfully reproduced the error.

Finding out the error trigger

looking for

I checked to make sure it was the same error, then I looked for the trigger. It pointed towards an attribute not being found from the Page instance. An attribute that didn't exist in the schema.

Interpreting the error

ASL

The AttributeError happening only after shell manipulation meant the views file was responsible for that 'extra' attribute because shell commands run independently of views.

Finding the culprit block of code

Reading the AgingPagesView class, I noticed get_queryset() annotated a last_published_by that was assigned a value of a query from a PageLogEntry model. This model created an instance every time a Page was 'published' from the dashboard. I also noticed the decorate_paginated_queryset called a method add_last_publisher_name_to_page for each page in the queryset returned.

Making the fix

All the add_last_publisher_name_to_page() did was check if last_published_by existed, give a user_id value, and then create a last_published_by_user field for the page (which wouldn't exist if the page was created from the shell). So, my mind-blowingly fantastic fix:

#... The code already there
else:
   page.last_published_by_user = ""
Enter fullscreen mode Exit fullscreen mode

Genius, I know. 😁
Assigning an empty string value would stop the crash, without breaking existing logic.

TL;DR

This fixed that.
It was a big problem that was fixed by a seemingly small change, but that small change was hidden behind a lot of complexity.

Thanks for reading.
Cheers.

Top comments (0)