Search code examples
djangodjango-class-based-viewsdjango-serializer

Serializing context on Django CBV?


I'm having issues serializing a 3rd party package (django-organizations) as I want to receive the context in JSON.

The class itself looks like this:

class OrganizationUserMixin(OrganizationMixin, JSONResponseMixin):
    """Mixin used like a SingleObjectMixin to fetch an organization user"""

    user_model = OrganizationUser
    org_user_context_name = 'organization_user'

    def get_user_model(self):
        return self.user_model

    def get_context_data(self, **kwargs):
        kwargs = super(OrganizationUserMixin, self).get_context_data(**kwargs)
        kwargs.update({self.org_user_context_name: self.object,
            self.org_context_name: self.object.organization})
        return kwargs

    def get_object(self):
        """ Returns the OrganizationUser object based on the primary keys for both
        the organization and the organization user.
        """
        if hasattr(self, 'organization_user'):
            return self.organization_user
        organization_pk = self.kwargs.get('organization_pk', None)
        user_pk = self.kwargs.get('user_pk', None)
        self.organization_user = get_object_or_404(
                self.get_user_model().objects.select_related(),
                user__pk=user_pk, organization__pk=organization_pk)
        return self.organization_user

And I'm trying to pass this custom JSONResponseMixin to my OrganizationUserMixin class:

class JSONResponseMixin:
    """
    A mixin that can be used to render a JSON response.
    """
    def render_to_json_response(self, context, **response_kwargs):
        """
        Returns a JSON response, transforming 'context' to make the payload.
        """
        return JsonResponse(
            self.get_data(context),
            **response_kwargs
        )

    def get_data(self, context):
        print(context)
        return context

And then overriding the render_to_response in OrganizationUserMixin as such:

def render_to_response(self, context, **response_kwargs):
    return self.render_to_json_response(context, **response_kwargs)

If I print context

It looks something like this

# {
# 'object': <OrganizationUser: Erik (MyOrgName)>, 
# 'organizationuser': <OrganizationUser: Erik (MyOrgName)>, 
# 'organization': <Organization: MyOrgName>, 
# 'view': <organizations.views.OrganizationUserDetail object at 0x1091a3ac0>, 
# 'organization_user': <OrganizationUser: Erik (MyOrgName)>
# }

The error message I get is TypeError: Object of type OrganizationUser is not JSON serializable

How can I serialize the context in my JSONResponseMixin?


Solution

  • You have two options here, either use Django rest framework (DRF) or implement functions that performs serialization for the models.

    Option 1

    DRF is a more sustainable solution as you grow the API side of your application, as it would abstract most of de/serialization work, and provide you with alot of other useful functionalities, such as Routers, ViewSets, and other.

    Example Code

    # serializers.py
    from rest_framework import serializers
    
    class OrganizationUserSerializer(serializers.ModelSerializer):
        class Meta:
            model = OrganizationUser
            fields = '__all__'
    
    
    # views.py
    from rest_framework import generics
    
    class OrganizationUser(generics.RetrieveModelMixin):
        queryset = OrganizationUser.objects.all()
        serializer_class = OrganizationUserSerializer
    

    Option 2

    That being said, if you the JsonResponseMixin is sufficient for most of your needs, and your application is not mainly reliant on the API, you can get away with just adding serialization functions for your models and calling them in your JsonResponseMixin.get_data()

    Example code:

    # Models.py
    class OrganizationUser(models.Model):
        ...
    
        def to_json(self):
            # assuming you have a field name and organization
            return {"name": self.name, "organization": self.organization.to_json()}
    
    # mixins.py
    class JSONResponseMixin:
        """
        A mixin that can be used to render a JSON response.
        """
        def render_to_json_response(self, context, **response_kwargs):
            """
            Returns a JSON response, transforming 'context' to make the payload.
            """
            return JsonResponse(
                self.get_data(context),
                **response_kwargs
            )
    
        def get_data(self, context):
            data = {}
            for key, val in context:
                if hasattr(val, "to_json"):
                    data[key] = val.to_json()
                else:
                    data[key] = val
            return data