django-rest-frameworkwagtailwagtail-streamfield

Wagtail custom API Endpoint doesn't "render / display" streamfield correctly


I'm trying to implement a settings option for social media accounts. Therefore, I created a simple streamfield and registered a class as setting. Took some time as I ran into a bug.

That's why I'm curious if the following is a bug or if I just missed something important.

The SocialMediaSettings class:

@register_setting
class SocialMediaSettings(BaseGenericSetting):
    body = StreamField([
        ("social_account", blocks.SocialBlock()),
    ], null=True, blank=True, use_json_field=True)

    api_fields = [
        APIField("body"),
    ]

    panels = [
        FieldPanel("body")
    ]

The custom API endpoint:

from rest_framework import viewsets
from rest_framework import serializers
from django.urls import re_path

from site_settings.models import SocialMediaSettings


class SocialMediaSettingsSerializer(serializers.ModelSerializer):
    class Meta:
        model = SocialMediaSettings
        fields = '__all__'


class SocialMediaSettingsViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = SocialMediaSettings.objects.all()
    serializer_class = SocialMediaSettingsSerializer

    @classmethod
    def get_urlpatterns(cls):
        """
        This returns a list of URL patterns for the endpoint
        """
        return [
            re_path(r'^$', cls.as_view({'get': 'list'})),
        ]

As well registered in the API:

api_router.register_endpoint('socials', SocialMediaSettingsViewSet)

Opening the API this all results in the following:

[
    {
        "id": 1,
        "body": "[{\"type\": \"social_account\", \"value\": {\"name\": \"Name\", \"url\": \"https://google.de\", \"icon\": \"iconClass\"}, \"id\": \"8b8a9e45-3fc4-400a-8ed2-7d8b1abba647\"}]"
    }
]

I mean, the body is there and the content as well. But this is how it's stored in the database I guess. If I use the streamfield in a normal page, with the default pages API endpoint it's returned this way (which is what I want):

[
   {
    "id": 1,
    "body": [
            {
                "type": "social_account",
                "value": {
                    "name": "Name",
                    "url": "https://google.de",
                    "icon": "iconClass"
                },
                "id": "a0d35fb8-f4dd-4ea0-b3e1-cd10236e72db"
            }
        ]
    }
]

Did I miss something in my API endpoint? Or is it just broken in Wagtail 4.0.1 (Django 4.0)?


Solution

  • from rest_framework import serializers
    
    class SocialMediaSettingsSerializer(serializers.ModelSerializer):
    

    Try changing your serializer to inherit from the BaseSerializer used for pages:

    from wagtail.api.v2.serializers import BaseSerializer
    
    class SocialMediaSettingsSerializer(BaseSerializer):
    

    The BaseSerializer inherits from DRF's ModelSerializer but adds code to customize StreamField serialization.