This commit is contained in:
parent
1aad42cb83
commit
21c4d803dd
1 changed files with 19 additions and 17 deletions
34
slides.md
34
slides.md
|
@ -33,12 +33,12 @@ background: /intranet.png
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
- People usually use Wagtail as a website or blog
|
- 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")
|
- At Torchbox, we use it for internal documentation ("intranet")
|
||||||
- Processes
|
- Processes
|
||||||
- Company information
|
- Company information
|
||||||
- Links to other places etc
|
- Links to other places etc
|
||||||
- Been around for a while
|
- Been around for a while
|
||||||
|
- Slowly growing
|
||||||
- In 2022, we restructured the content
|
- In 2022, we restructured the content
|
||||||
- Make it easier to find things
|
- Make it easier to find things
|
||||||
- Remove duplication
|
- Remove duplication
|
||||||
|
@ -56,11 +56,12 @@ background: /site-history.png
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
- First step: Understanding what happened
|
- First step: Understanding what happened
|
||||||
|
- Wagtail has me covered there
|
||||||
- The site history report!
|
- 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
|
- One staff member deleted the "Sysadmin" section a few days before
|
||||||
- Which deleted every page under it, all 105 of them
|
- Which deleted every page under it
|
||||||
- "Radical reorganisation"
|
- 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'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
|
- 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
|
- 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.
|
- 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.
|
- 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!
|
- With 0 downtime, too!
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
@ -113,7 +115,7 @@ layout: section
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
- We backup our intranet nightly, so I downloaded a backup from before the incident
|
- 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`.
|
- 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
|
- 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,7 +162,7 @@ collector.collect(list(child_pages) + [sysadmin_page])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This is where the magic happens
|
- This is where the magic happens
|
||||||
- Deleting a page deletes more than just a page
|
- Deleting a page deletes more than just a page
|
||||||
- The specific model
|
- The specific model
|
||||||
- Revisions
|
- Revisions
|
||||||
|
@ -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
|
- `collector.data` now contains all the model instances which were deleted, in memory on my laptop
|
||||||
- My laptop isn't what's running production
|
- 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
|
- 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
|
- 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
|
- 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
|
- 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
|
- However, `NestedObjects` still finds these through tables, and tries to load them separately
|
||||||
- Resulting in duplicate objects and referential integrity issues
|
- 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`
|
- The search index objects (we use postgres) were picked up by `NestedObjects`
|
||||||
- They didn't like being restored
|
- They didn't like being restored
|
||||||
- So I skipped them and moved on, knowing I'd just rebuild the index later.
|
- 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
|
- The tense bit
|
||||||
- Once I was happy, I ran the same steps on production
|
- 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.
|
- 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]Before I began, I did a backup
|
||||||
- [click]With the data file in place, [click]I crossed everything and ran `loaddata`
|
- 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
|
- Pages popped up in the admin as if they never left
|
||||||
- [click]`checktree` worked.
|
- [click]`checktree` worked.
|
||||||
- [click]`update_index` worked.
|
- [click]`update_index` worked.
|
||||||
- [click] As did `rebuild_reference_index`
|
- [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 content freeze
|
||||||
- No data loss
|
- No data loss
|
||||||
- Most people didn't even know there was an issue
|
- 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
|
- 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!
|
- Hopefully this helps you out as much as it has me!
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue