kopia lustrzana https://github.com/wagtail/wagtail
make streamfield migration names from operations
- `operation_name_fragment` property added to operations - `migration_name_fragment` property added to MigrateStreamData - added tests, docs updated - BaseBlockOperation inherits from `abc.ABC`pull/9912/head
rodzic
55d04366f7
commit
3ed84b36da
|
@ -483,7 +483,15 @@ Block ids are not preserved here since the new blocks are structurally different
|
|||
|
||||
#### Basic usage
|
||||
|
||||
While this package comes with a set of operations for common use cases, there may be many instances where you need to define your own operation for mapping data. Making a custom operation is fairly straightforward. All you need to do is extend the `BaseBlockOperation` class and define the `apply` method.
|
||||
While this package comes with a set of operations for common use cases, there may be many instances where you need to define your own operation for mapping data. Making a custom operation is fairly straightforward. All you need to do is extend the `BaseBlockOperation` class and define the required methods,
|
||||
|
||||
- `apply`
|
||||
This applies the actual changes on the existing block value and returns the new block value.
|
||||
- `operation_name_fragment`
|
||||
(`@property`) Returns a name to be used for generating migration names.
|
||||
|
||||
(**NOTE:** `BaseBlockOperation` inherits from `abc.ABC`, so all of the required methods
|
||||
mentioned above have to be defined on any class inheriting from it.)
|
||||
|
||||
For example, if we want to truncate the string in a `CharBlock` to a given length,
|
||||
|
||||
|
@ -501,6 +509,11 @@ class MyBlockOperation(BaseBlockOperation):
|
|||
new_block_value = block_value[:self.length]
|
||||
return new_block_value
|
||||
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "truncate_{}".format(self.length)
|
||||
|
||||
```
|
||||
|
||||
#### block_value
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import json
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from django.db.models import JSONField, F, Q, Subquery, OuterRef
|
||||
from django.db.models.functions import Cast
|
||||
from django.db.migrations import RunPython
|
||||
|
@ -78,6 +79,16 @@ class MigrateStreamData(RunPython):
|
|||
|
||||
return (self.__class__.__qualname__, args, kwargs)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
# We are using an OrderedDict here to essentially get the functionality of an ordered set
|
||||
# so that names generated will be consistent.
|
||||
fragments = OrderedDict(
|
||||
(op.operation_name_fragment, None)
|
||||
for op, _ in self.operations_and_block_paths
|
||||
)
|
||||
return "_".join(fragments.keys())
|
||||
|
||||
def migrate_stream_data_forward(self, apps, schema_editor):
|
||||
model = apps.get_model(self.app_name, self.model_name)
|
||||
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from wagtail.blocks.migrations.utils import formatted_list_child_generator
|
||||
from django.utils.deconstruct import deconstructible
|
||||
|
||||
|
||||
class BaseBlockOperation:
|
||||
class BaseBlockOperation(ABC):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def apply(self, block_value):
|
||||
raise NotImplementedError
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def operation_name_fragment(self):
|
||||
pass
|
||||
|
||||
|
||||
@deconstructible
|
||||
|
@ -37,6 +44,10 @@ class RenameStreamChildrenOperation(BaseBlockOperation):
|
|||
mapped_block_value.append(child_block)
|
||||
return mapped_block_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "rename_{}_to_{}".format(self.old_name, self.new_name)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class RenameStructChildrenOperation(BaseBlockOperation):
|
||||
|
@ -65,6 +76,10 @@ class RenameStructChildrenOperation(BaseBlockOperation):
|
|||
mapped_block_value[child_key] = child_value
|
||||
return mapped_block_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "rename_{}_to_{}".format(self.old_name, self.new_name)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class RemoveStreamChildrenOperation(BaseBlockOperation):
|
||||
|
@ -89,6 +104,10 @@ class RemoveStreamChildrenOperation(BaseBlockOperation):
|
|||
if child_block["type"] != self.name
|
||||
]
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "remove_{}".format(self.name)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class RemoveStructChildrenOperation(BaseBlockOperation):
|
||||
|
@ -113,6 +132,10 @@ class RemoveStructChildrenOperation(BaseBlockOperation):
|
|||
if child_key != self.name
|
||||
}
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "remove_{}".format(self.name)
|
||||
|
||||
|
||||
class StreamChildrenToListBlockOperation(BaseBlockOperation):
|
||||
"""Combines StreamBlock children of the given type into a new ListBlock
|
||||
|
@ -151,6 +174,10 @@ class StreamChildrenToListBlockOperation(BaseBlockOperation):
|
|||
new_temp_blocks.append({**block, "type": "item"})
|
||||
self.temp_blocks = new_temp_blocks
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "{}_to_list_block_{}".format(self.block_name, self.list_block_name)
|
||||
|
||||
|
||||
class StreamChildrenToStreamBlockOperation(BaseBlockOperation):
|
||||
"""Combines StreamBlock children of the given types into a new StreamBlock
|
||||
|
@ -183,6 +210,10 @@ class StreamChildrenToStreamBlockOperation(BaseBlockOperation):
|
|||
mapped_block_value.append(new_stream_block)
|
||||
return mapped_block_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "{}_to_stream_block".format("_".join(self.block_names))
|
||||
|
||||
|
||||
class AlterBlockValueOperation(BaseBlockOperation):
|
||||
"""Alters the value of each block to the given value
|
||||
|
@ -198,6 +229,10 @@ class AlterBlockValueOperation(BaseBlockOperation):
|
|||
def apply(self, block_value):
|
||||
return self.new_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "alter_block_value"
|
||||
|
||||
|
||||
class StreamChildrenToStructBlockOperation(BaseBlockOperation):
|
||||
"""Move each StreamBlock child of the given type inside a new StructBlock
|
||||
|
@ -266,6 +301,10 @@ class StreamChildrenToStructBlockOperation(BaseBlockOperation):
|
|||
mapped_block_value.append(child_block)
|
||||
return mapped_block_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "{}_to_struct_block_{}".format(self.block_name, self.struct_block_name)
|
||||
|
||||
|
||||
class ListChildrenToStructBlockOperation(BaseBlockOperation):
|
||||
def __init__(self, block_name):
|
||||
|
@ -282,3 +321,7 @@ class ListChildrenToStructBlockOperation(BaseBlockOperation):
|
|||
{**child_block, "value": {self.block_name: child_block["value"]}}
|
||||
)
|
||||
return mapped_block_value
|
||||
|
||||
@property
|
||||
def operation_name_fragment(self):
|
||||
return "list_block_items_to_{}".format(self.block_name)
|
||||
|
|
|
@ -10,11 +10,7 @@ class MigrationTestMixin:
|
|||
default_operation_and_block_path = []
|
||||
app_name = None
|
||||
|
||||
def apply_migration(
|
||||
self,
|
||||
revisions_from=None,
|
||||
operations_and_block_path=None,
|
||||
):
|
||||
def init_migration(self, revisions_from=None, operations_and_block_path=None):
|
||||
migration = Migration(
|
||||
"test_migration", "wagtail_streamfield_migration_toolkit_test"
|
||||
)
|
||||
|
@ -28,6 +24,18 @@ class MigrationTestMixin:
|
|||
)
|
||||
migration.operations = [migration_operation]
|
||||
|
||||
return migration
|
||||
|
||||
def apply_migration(
|
||||
self,
|
||||
revisions_from=None,
|
||||
operations_and_block_path=None,
|
||||
):
|
||||
migration = self.init_migration(
|
||||
revisions_from=revisions_from,
|
||||
operations_and_block_path=operations_and_block_path,
|
||||
)
|
||||
|
||||
loader = MigrationLoader(connection=connection)
|
||||
loader.build_graph()
|
||||
project_state = loader.project_state()
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from wagtail.blocks.migrations.operations import (
|
||||
RemoveStreamChildrenOperation,
|
||||
RenameStreamChildrenOperation,
|
||||
)
|
||||
from wagtail.test.streamfield_migrations import models
|
||||
from wagtail.test.streamfield_migrations.testutils import MigrationTestMixin
|
||||
|
||||
|
||||
class MigrationNameTest(TestCase, MigrationTestMixin):
|
||||
model = models.SamplePage
|
||||
app_name = "wagtail_streamfield_migration_toolkit_test"
|
||||
|
||||
def test_rename(self):
|
||||
operations_and_block_path = [
|
||||
(
|
||||
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
|
||||
"",
|
||||
)
|
||||
]
|
||||
migration = self.init_migration(
|
||||
operations_and_block_path=operations_and_block_path
|
||||
)
|
||||
|
||||
suggested_name = migration.suggest_name()
|
||||
self.assertEqual(suggested_name, "rename_char1_to_renamed1")
|
||||
|
||||
def test_remove(self):
|
||||
operations_and_block_path = [
|
||||
(
|
||||
RemoveStreamChildrenOperation(name="char1"),
|
||||
"",
|
||||
)
|
||||
]
|
||||
migration = self.init_migration(
|
||||
operations_and_block_path=operations_and_block_path
|
||||
)
|
||||
|
||||
suggested_name = migration.suggest_name()
|
||||
self.assertEqual(suggested_name, "remove_char1")
|
||||
|
||||
def test_multiple(self):
|
||||
operations_and_block_path = [
|
||||
(
|
||||
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
|
||||
"",
|
||||
),
|
||||
(
|
||||
RemoveStreamChildrenOperation(name="char1"),
|
||||
"simplestruct",
|
||||
),
|
||||
]
|
||||
migration = self.init_migration(
|
||||
operations_and_block_path=operations_and_block_path
|
||||
)
|
||||
|
||||
suggested_name = migration.suggest_name()
|
||||
self.assertEqual(suggested_name, "rename_char1_to_renamed1_remove_char1")
|
Ładowanie…
Reference in New Issue