Skip to content

Commit 099efef

Browse files
author
Andy Babic
committed
Update README
1 parent b63d2b0 commit 099efef

File tree

1 file changed

+103
-29
lines changed

1 file changed

+103
-29
lines changed

README.md

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,111 @@
1-
# Migration-Lite StreamField
1+
# migration-lite-streamfield
22

3-
A lightweight StreamField implementation for Wagtail that helps manage database migrations more effectively.
3+
(Or `mlstreamfield` for short)
44

5-
## Overview
5+
All the goodness of Wagtail's Streamfield, but without the migration headaches.
66

7-
Migration-Lite StreamField is a Django field that extends Wagtail's StreamField functionality, focusing on making database migrations more manageable. It preserves raw field data during migrations, allowing for safer schema changes and content updates.
7+
## What problem does `mlstreamfield` solve?
88

9-
## Features
9+
Wagtail's `StreamField` is flexible and powerful, but that flexibility often comes at a price in client projects.
1010

11-
- Drop-in replacement for Wagtail's StreamField
12-
- Preserves raw data during migrations
13-
- Handles both JSON string and list formats
14-
- Compatible with Wagtail 6.2 and 6.3
15-
- Full test coverage
11+
The native implementation has included information about the blocks in a StreamField in migrations pretty much from the beignning. Personally speaking, it's a decision I've come to respect. It makes sense from a 'framework' perspective, because you never truly know how your code is being used, or what developers might need to do within a data migration. However, there's no denying it adds a degree of overhead to most client projects:
12+
13+
- Each time you add a new block, you must generate a migration for any model you decide to add it to.
14+
- Each time you change the definition of an existing block, you must generate a migration for any model that uses it.
15+
- Each time you decide to rename a block type or move it to a more suitable place in the codebase, you must generate a migration for any model that uses it.
16+
- Each time you change the label and help text for the field to improve the experience for editors, you must generate a migration to reflect those changes.
17+
18+
_Notice the pattern?_
19+
20+
You might not have realised, but all of these additional migrations slow a project down. Django evaluates a project's migration history regularly when running a number of key commands, including `makemigrations`, `migrate`, `runserver`, `test`, and whenever you run a custom management command. The more migrations you have, and the larger those migrations are, the longer that evaluation takes.
21+
22+
Whilst Wagtail has done some amazing work in recent versions to reduce the size of StreamFieldmigrations, the sheer number of migrations generated as part of the day-to-day development on a busy project can still be problematic. In addition to the aformentioned slow-down, there's also:
23+
24+
- The additional noise in code contributions
25+
- The higher likelihood of merge conflicts when multiple developers are working on common blocks at the same time.
26+
- The cumulative overhead of having to apply and reverse migrations when switching between multiple feature branches your are working on (or reviewing).
27+
28+
Let's also not forget those regretable 'environment-specific' merge migrations that are sometimes required to bring environments back to life when features are merged to an environment-specific branch in a different order to trunk (eek!).
29+
30+
## How is `mlstreamfield` different?
31+
32+
It's strength comes simply from _not including block definitions (or references to them) in migrations_ - pure and simple. It extends Wagtail's StreamField, so you get the same great editor experience, power and flexibility. Things just work a little differently when it comes to migrations.
33+
34+
## Frequently Asked Questions
35+
36+
### Cut to the chase! What will I lose by switching to `mlstreamfield`?
37+
38+
Direct - I like it! Let me sum it up for you very clearly:
39+
40+
#### 1. You'll only be able to interact with raw data values in data migrations (accessed via `fieldname.raw_data`).
41+
42+
If, like me, you've written a lot of data migrations over the years, this is probably all you're interested in anyway! However, for those worried about the implications, the basic rundown is:
43+
44+
- `RichTextBlock` values will just be strings.
45+
- `DateBlock`, `DateTimeBlock` and `TimeBlock` values will just be strings in ISO format.
46+
- `ChooserBlock` values will just be strings (the ID of the chosen item).
47+
- `StructBlock` values will just be dicts of raw values.
48+
- `ListBlock` and `StreamBlock` values will just be lists of dicts representing each item.
49+
50+
The values are still accessible, and modifiable: You might just need to convert the odd ID into an object yourself, or use `datetime.date.fromisoformat()` to convert a date string into a date here and there.
51+
52+
Changes made directly to `fieldname.raw_data` are reflected when the object is saved, so it's honestly the easiest way to interact with field values in migrations anyway (regardless of whether you want to use this package or not).
53+
54+
This is barely worth mentioning, but lack of access to block definitions also means you won't be able to 'render' `StreamField` values in a data migration. However, that would be a very strange thing to need in data migration anyway.
55+
56+
#### 2. Some of the special 'migration operations' for StreamFields might not work as expectedafter switching
57+
58+
Some of the built-in migration operations mentioned in [the Wagtail documentation](https://docs.wagtail.org/en/stable/advanced_topics/streamfield_migrations.html#why-are-data-migrations-necessary), or in packages like [wagtail-streamfield-migration-toolkit](https://github.com/wagtail/wagtail-streamfield-migration-toolkit) might not work as expected.
59+
60+
Honestly though, once you get a handle on the few common data structures encountered in raw `StreamField` values, achieving something similar with `RunPython` is usually quite straightforward. Plus, understanding the raw data format of `StreamField` values is a skill that will help you with other aspects of development.
61+
62+
#### 3. It's an additional project dependency to manage
63+
64+
Although the entirety of the package is a single Python class in a single module, it's still an additional thing to consider when keeping your Wagtail version up-to-date.
65+
66+
That said, because of the tiny scope of the package, and the stability of the `StreamField` API (remember, field classes are regularly referenced by historic migrations,s), it's unlikely that a lot of changes will be needed to keep things compatible with the latest Wagtail version.
67+
68+
At Torchbox, we've been using a version of this field in client projects for a few years, and only had to change anything for the Wagtail 6.2 release (most ).
69+
70+
4. **That's it!**
71+
72+
### Q: Will I lose existing field data when switching to `mlstreamfield`?
73+
74+
**Absolutely not**. `mlstreamfield.StreamField` is a drop-in replacement for the native version, and changes nothing about how data is stored internally. Any existing data is preserved. If you need proof, take a look through the [testapp migrations](https://github.com/torchbox/migration-lite-streamfield/tree/main/tests/testapp/migrations) for this package (It's thoroughly tested).
75+
76+
### Q: Will switching to `mlstreamfield` break my migration history?
77+
78+
**No**. You'll need to generate new migrations to account for any fields you've switched over, because you're changing the field type. But, adopting Migration-Lite StreamField doesn't touch historic migrations in any way, or change how migrations in general work.
79+
80+
Historic migrations will continue to use Wagtail's native `StreamField` for all operations up until the migration that changes the field type to `mlstreamfield.StreamField`.
81+
82+
### Q: Will switching to `mlstreamfield` prevent me from writing data migrations when a client changes their mind about something?
83+
84+
**No**. You can still access and modify field data in data migrations. You just need to use `fieldname.raw_data` to access it. If you want to see some examples, take a look at the [testapp migrations](https://github.com/torchbox/migration-lite-streamfield/tree/main/tests/testapp/migrations) for this package.
85+
86+
### Q: Will switching to `mlstreamfield` shrink my existing migration files?
87+
88+
**No**. Your historic migrations are completely unaffected. Switching to `mlstreamfield` will prevent things getting any worse, but it can't solve historic migrationproblems. That can only really be acheived through [migration squashing](https://medium.com/@SmoQ/django-squashing-database-migrations-4906e4beeb66).
89+
90+
The _earlier_ in a project you adopt `mlstreamfield`, the more you'll profit.
91+
92+
### Q: Can I use `mlstreamfield` in my Wagtail add-on package?
93+
94+
Technically, _yes_. But, the recommendation is to avoid it.
95+
96+
An add-on package doesn't usually suffer from the same issues as a client project (rapid development with multiple developers, experimental POCs, direction changes etc), so you're less likely to benefit from it.
97+
98+
Also, users of your package could feel blindsided by the change in migration behaviour if they've haven't adopted `mlstreamfield` themselves, and aren't familiar with the nuances.
99+
100+
### Q: Will I lose field data if I write a bad data migration?
101+
102+
**Yes**. But, that's true whether you decied to use `mlstreamfield` or not. Data migrations are notoriously tricky territory and require careful testing locally before committing. Nothing about `mlstreamfield` changes that.
103+
104+
## Requirements
105+
106+
- Python 3.11+
107+
- Django 4.2+
108+
- Wagtail 5.2+
16109

17110
## Installation
18111

@@ -49,20 +142,6 @@ class BlogPage(Page):
49142
], use_json_field=True)
50143
```
51144

52-
## Migration Support
53-
54-
During migrations, the field:
55-
- Preserves raw field data
56-
- Handles both JSON strings and list formats
57-
- Safely stores invalid JSON as raw text for error handling
58-
- Maintains compatibility with Wagtail's migration system
59-
60-
## Requirements
61-
62-
- Python 3.8+
63-
- Django 4.2+
64-
- Wagtail 6.2+
65-
66145
## Contributing
67146

68147
Contributions are welcome! Please feel free to submit a Pull Request.
@@ -75,8 +154,3 @@ This project is licensed under the MIT License - see the LICENSE file for detail
75154

76155
Developed and maintained by [Torchbox](https://torchbox.com/).
77156
```
78-
79-
</rewritten_file>
80-
```
81-
82-
</rewritten_file>

0 commit comments

Comments
 (0)