151 lines
1.9 KiB
Markdown
151 lines
1.9 KiB
Markdown
|
---
|
||
|
title: Recovering deleted Wagtail pages and Django models
|
||
|
class: text-center
|
||
|
highlighter: shiki
|
||
|
transition: slide-left
|
||
|
---
|
||
|
|
||
|
# Recovering deleted Wagtail pages and Django models
|
||
|
|
||
|
### Jake Howard
|
||
|
|
||
|
###### Senior Systems Engineer @ Torchbox
|
||
|
###### Security Team @ Wagtail
|
||
|
###### Performance Working Group @ Wagtail
|
||
|
|
||
|
---
|
||
|
layout: cover
|
||
|
background: https://cdn.jsdelivr.net/gh/wagtail/wagtail@main/.github/wagtail-screenshot-with-browser.png
|
||
|
---
|
||
|
|
||
|
# Setting the scene
|
||
|
|
||
|
---
|
||
|
layout: cover
|
||
|
---
|
||
|
|
||
|
# Site history report
|
||
|
|
||
|
---
|
||
|
layout: cover
|
||
|
---
|
||
|
|
||
|
# Restoring from backups
|
||
|
|
||
|
---
|
||
|
layout: cover
|
||
|
---
|
||
|
|
||
|
# _Partial_ restores from backups
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
# 1. Spin up a database backup
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
<style>
|
||
|
pre.shiki {
|
||
|
font-size: 1.4rem;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
# 2. Locate the page models
|
||
|
|
||
|
<div class="pt-5">
|
||
|
|
||
|
```python
|
||
|
from wagtail.models import Page
|
||
|
|
||
|
sysadmin_page = Page.objects.get(id=91)
|
||
|
|
||
|
child_pages = sysadmin_page.get_descendants()
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
<style>
|
||
|
pre.shiki {
|
||
|
font-size: 1.4rem;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
# 3. Locate what was deleted
|
||
|
|
||
|
<div class="pt-5">
|
||
|
|
||
|
```python
|
||
|
from django.contrib.admin.utils import NestedObjects
|
||
|
|
||
|
collector = NestedObjects()
|
||
|
collector.collect(list(child_pages) + [sysadmin_page])
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
<style>
|
||
|
pre.shiki {
|
||
|
font-size: 1.4rem;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
# 4. Serialize
|
||
|
|
||
|
<div class="pt-5">
|
||
|
|
||
|
```python
|
||
|
from django.core import serializers
|
||
|
|
||
|
class NoM2MSerializer(Serializer):
|
||
|
def handle_m2m_field(self, obj, field):
|
||
|
pass
|
||
|
|
||
|
def get_model_instances():
|
||
|
for qs in collector.data.values():
|
||
|
yield from qs
|
||
|
|
||
|
with open("deleted-models.json", "w") as f:
|
||
|
NoM2MSerializer().serialize(
|
||
|
get_model_instances(),
|
||
|
stream=f
|
||
|
)
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
# 4a. Deserialize
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
# 5. Test test test
|
||
|
|
||
|
---
|
||
|
layout: center
|
||
|
---
|
||
|
|
||
|
# 6. Showtime!
|
||
|
|
||
|
---
|
||
|
layout: end
|
||
|
---
|
||
|
|
||
|
# Conclusion
|