1
Fork 0

Tweak script
All checks were successful
/ build (push) Successful in 2m20s

This commit is contained in:
Jake Howard 2024-06-12 10:20:21 +01:00
parent 1aad42cb83
commit 21c4d803dd
Signed by: jake
GPG key ID: 57AFB45680EDD477

View file

@ -33,12 +33,12 @@ background: /intranet.png
<!--
- People usually use Wagtail as a website or blog
- But it works really well as an intranet too
- At Torchbox, we use it for internal documentation ("intranet")
- Processes
- Company information
- Links to other places etc
- Been around for a while
- Slowly growing
- In 2022, we restructured the content
- Make it easier to find things
- Remove duplication
@ -56,11 +56,12 @@ background: /site-history.png
<!--
- First step: Understanding what happened
- Wagtail has me covered there
- The site history report!
- Fortunately, Wagtail showed _almost_ exactly what had happened, and what I expected
- Fortunately, Wagtail showed _almost_ exactly what had happened
- One staff member deleted the "Sysadmin" section a few days before
- Which deleted every page under it, all 105 of them
- "Radical reorganisation"
- Which deleted every page under it
- All 105 of them
-->
---
@ -76,6 +77,7 @@ backgroundSize: contain
- They'd made a new "Sysadmin" section a while ago, before switching strategy to move pages in the existing tree
- They then deleted the wrong one
- Sure, Wagtail shows a confirmation when you're deleting pages, but when you're deleting a lot of pages, and expecting to delete pages, you might not read the message perfectly
- "Radical reorganisation" my boss called it
- With the content gone, I had to restore from backups.
-->
@ -100,7 +102,7 @@ layout: section
<!--
- Ideally, what I needed was to restore only the sysadmin pages, leaving all others completely untouched.
- Using a few tricks of Django and Wagtail internals, it's absolutely possible, and we did it
- Using a few tricks of Django and Wagtail internals, it's absolutely possible, and I did it
- With 0 downtime, too!
-->
@ -113,7 +115,7 @@ layout: section
<!--
- We backup our intranet nightly, so I downloaded a backup from before the incident
- Start the codebase locally so I can interrogate it
- Spin up the codebase locally so I can interrogate it
-->
---
@ -138,7 +140,7 @@ child_pages = sysadmin_page.get_descendants()
<!--
- Behind the scenes, Wagtail pages are a tree, implemented using `django-treebeard`.
- When a page is deleted, treebeard is the one who finds all the child pages and deletes them too
- And then Django and postgres deal with cascading the delete
- And then Django and the database deal with cascading the delete
-->
---
@ -160,13 +162,13 @@ collector.collect(list(child_pages) + [sysadmin_page])
</div>
<!--
This is where the magic happens
- This is where the magic happens
- Deleting a page deletes more than just a page
- The specific model
- Revisions
- Related models
- Through tables
- `get_descendants` won't get all those
- `get_descendants` won't get all those
- Calling `.delete` gives you the number of objects, and it's quite a lot
- If you've ever used the Django admin, you know it's capable of finding every model instance before a delete
- That's implemented with an undocumented but simple to use API
@ -212,7 +214,7 @@ with open("deleted-models.json", "w") as f:
<!--
- `collector.data` now contains all the model instances which were deleted, in memory on my laptop
- My laptop isn't what's running production
- Need to serialize the models into an intermdiary format which can be then be loaded onto production
- Need to serialize the models so they can be then be loaded onto production
- If you're thinking of fixtures, you're right
- Django's fixtures create a JSON representation of a model, so they can be saved in 1 location and loaded into another
- Mostly useful for complex test fixtures (hence the name), but generally useful for cases like this
@ -220,7 +222,7 @@ with open("deleted-models.json", "w") as f:
- When Django serializes a model with a m2m which doesn't use a custom table, it inlines the definition, because it's easier to work with
- However, `NestedObjects` still finds these through tables, and tries to load them separately
- Resulting in duplicate objects and referential integrity issues
- Instead, we exclude them
- Instead, we exclude them, knowing we picked them up explicitly with `NestedObjects`
-->
---
@ -295,7 +297,6 @@ layout: fact
- The search index objects (we use postgres) were picked up by `NestedObjects`
- They didn't like being restored
- So I skipped them and moved on, knowing I'd just rebuild the index later.
- `manage.py fixtree` also reports any tree issues, which there weren't
-->
---
@ -322,13 +323,14 @@ class: flex justify-center flex-col items-center
- The tense bit
- Once I was happy, I ran the same steps on production
- Our intranet runs on Heroku, so I had to do a few dances to get the JSON file up there.
- [click]Before I began, I did a backup, because I'm a good sysadmin
- [click]With the data file in place, [click]I crossed everything and ran `loaddata`
- [click]Before I began, I did a backup
- Because I'm a good sysadmin
- [click]With the data file in place, I crossed everything[click] and ran `loaddata`
- Pages popped up in the admin as if they never left
- [click]`checktree` worked.
- [click]`update_index` worked.
- [click] As did `rebuild_reference_index`
- The new pages were now findable
- The new pages were now findable and searchable
-->
---
@ -344,9 +346,9 @@ background: /sysadmin.png
- No content freeze
- No data loss
- Most people didn't even know there was an issue
- I've used this trick a a few times in my career, for both Wagtail and plain Django sites
- I've used this trick a few times in my career, for both Wagtail and plain Django sites
- Ironically, just a few weeks after the blog post was published
- Works identically for Django sites, so long as you know how to reconstruct the delete query.
- Works identically for any Django project, so long as you know how to reconstruct the delete query.
- Hopefully this helps you out as much as it has me!
-->