diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7b92a4e..d579b30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog ========= +1.0.1 (2022-07-09) +================== + +* Added support for Django 4.0 +* Added support for custom serializer +* Added serializer `django_cms` to be able to serialize `filer.Image` 1.0.0 (2020-09-02) ================== diff --git a/README.rst b/README.rst index fa705ce..7a46efc 100644 --- a/README.rst +++ b/README.rst @@ -8,10 +8,10 @@ django CMS Transfer and import plugin data from a page or a placeholder. It does not support foreign key relations and won't import/export related data, such as `media `_. -.. note:: - +.. note:: + This project is endorsed by the `django CMS Association `_. - That means that it is officially accepted by the dCA as being in line with our roadmap vision and development/plugin policy. + That means that it is officially accepted by the dCA as being in line with our roadmap vision and development/plugin policy. Join us on `Slack `_. .. image:: preview.gif @@ -23,8 +23,8 @@ Contribute to this project and win rewards Because this is a an open-source project, we welcome everyone to `get involved in the project `_ and -`receive a reward `_ for their contribution. -Become part of a fantastic community and help us make django CMS the best CMS in the world. +`receive a reward `_ for their contribution. +Become part of a fantastic community and help us make django CMS the best CMS in the world. We'll be delighted to receive your feedback in the form of issues and pull requests. Before submitting your @@ -44,6 +44,14 @@ file for additional dependencies: |python| |django| |djangocms| +Custom serializer +------------ +To be able to define custom behavior for various plugins, you can define a custom serializer as following:: + + SERIALIZATION_MODULES = { + "django_cms": "djangocms_transfer.serializers.django_cms", + } + DJANGO_CMS_TRANSFER_SERIALIZER = "django_cms" Installation ------------ diff --git a/djangocms_transfer/datastructures.py b/djangocms_transfer/datastructures.py index 3252a93..a6a0591 100644 --- a/djangocms_transfer/datastructures.py +++ b/djangocms_transfer/datastructures.py @@ -2,13 +2,12 @@ from django.core.serializers import deserialize from django.db import transaction -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.functional import cached_property from cms.models import CMSPlugin -from .utils import get_plugin_model - +from .utils import get_plugin_model, get_serializer_name BaseArchivedPlugin = namedtuple( 'ArchivedPlugin', @@ -30,12 +29,12 @@ def model(self): @cached_property def deserialized_instance(self): data = { - 'model': force_text(self.model._meta), + 'model': force_str(self.model._meta), 'fields': self.data, } # TODO: Handle deserialization error - return list(deserialize('python', [data]))[0] + return list(deserialize(get_serializer_name(), [data]))[0] @transaction.atomic def restore(self, placeholder, language, parent=None, with_data=True): diff --git a/djangocms_transfer/helpers.py b/djangocms_transfer/helpers.py index 496138f..16d49a0 100644 --- a/djangocms_transfer/helpers.py +++ b/djangocms_transfer/helpers.py @@ -2,7 +2,7 @@ from django.core import serializers -from .utils import get_plugin_fields, get_plugin_model +from .utils import get_plugin_fields, get_plugin_model, get_serializer_name def get_bound_plugins(plugins): @@ -41,7 +41,7 @@ def get_plugin_data(plugin, only_meta=False): custom_data = None else: plugin_fields = get_plugin_fields(plugin.plugin_type) - _plugin_data = serializers.serialize('python', (plugin,), fields=plugin_fields)[0] + _plugin_data = serializers.serialize(get_serializer_name(), (plugin,), fields=plugin_fields)[0] custom_data = _plugin_data['fields'] plugin_data = { diff --git a/djangocms_transfer/serializers/__init__.py b/djangocms_transfer/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djangocms_transfer/serializers/django_cms.py b/djangocms_transfer/serializers/django_cms.py new file mode 100644 index 0000000..7a49501 --- /dev/null +++ b/djangocms_transfer/serializers/django_cms.py @@ -0,0 +1,77 @@ +import base64 +import io + +from django.core.files import File as DjangoFile +from django.core.serializers import base +from django.core.serializers.python import ( + Serializer as PythonSerializer, + Deserializer as PythonDeserializer, + _get_model, +) + +try: + from filer.fields.image import FilerImageField + from filer.models import Image + + has_filer = True +except ImportError: + has_filer = False + + +class FilerImageFieldSerializer: + @classmethod + def serialize(cls, field_instance): + serializer = Serializer() + _image_plugin_data = serializer.serialize((field_instance,))[0] + _file_plugin_data = serializer.serialize( + (field_instance.file_ptr,), fields=["original_filename", "mime_type"] + )[0] + base64_image = base64.b64encode(field_instance.file.read()) + + _plugin_data = _image_plugin_data["fields"] + _plugin_data.update(_file_plugin_data["fields"]) + _plugin_data["file_content"] = base64_image.decode() + return _plugin_data + + @classmethod + def deserialize(cls, serialized_data): + base64_image = base64.b64decode(serialized_data.pop("file_content")) + + filename = serialized_data["original_filename"] + file_obj = DjangoFile(io.BytesIO(base64_image), name=filename) + image = Image.objects.create( + **serialized_data, + file=file_obj, + ) + + return image.pk + + +class Serializer(PythonSerializer): + def handle_fk_field(self, obj, field): + if has_filer and isinstance(field, FilerImageField): + field_instance = getattr(obj, field.name) + self._current[field.name] = FilerImageFieldSerializer.serialize( + field_instance + ) + else: + super(Serializer, self).handle_fk_field(obj, field) + + +def Deserializer(object_list, **options): + for d in object_list: + # Look up the model and starting build a dict of data for it. + try: + Model = _get_model(d["model"]) + except base.DeserializationError: + if options["ignorenonexistent"]: + continue + else: + raise + for (field_name, field_value) in d["fields"].items(): + field = Model._meta.get_field(field_name) + if has_filer and isinstance(field, FilerImageField): + value = FilerImageFieldSerializer.deserialize(field_value) + d["fields"][field_name] = value + + yield from PythonDeserializer(object_list, **options) diff --git a/djangocms_transfer/utils.py b/djangocms_transfer/utils.py index c7528b4..8ef0420 100644 --- a/djangocms_transfer/utils.py +++ b/djangocms_transfer/utils.py @@ -34,3 +34,8 @@ def get_plugin_fields(plugin_type): @lru_cache() def get_plugin_model(plugin_type): return get_plugin_class(plugin_type).model + + +def get_serializer_name(default='python'): + from django.conf import settings + return getattr(settings, 'DJANGO_CMS_TRANSFER_SERIALIZER', default)