django-api-domains icon indicating copy to clipboard operation
django-api-domains copied to clipboard

[QUESTION] How to use serializers from other domains for nested relationships

Open juanxo opened this issue 6 years ago • 5 comments

While trying to implement the structure that you have posed, I have reached a point where I wonder what would be the best course of action:

In my use case, I have two domains, one that includes a User model, and another one that includes an Outfit model. I'm also using django serializers atm as they provide some helpers to ease development.

For my use case, I want to return the User model nested inside the Outfit model, but that would cross models between domains. What would be the best approach here?

from django.contrib.auth.models import User
from .models.look import Outfit

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'email', 'url']

class OutfitSerializer(serializers.HyperlinkedModelSerializer):
    user = UserSerializer()

    class Meta:
        model = Look
        fields = ['shortcode', 'user', 'url']

juanxo avatar Aug 17 '19 12:08 juanxo

This is a good question, and one my team has struggled with solving too. I briefly looked at some solutions around the internet. Things like protocol buffers try and solve this by setting up a shared resource of schemas for data. My only concern with that is: you then have the problem of controlling the distribution of that protocol buffer.

We solved this (we === the team at my current employment) by having our own UserSerializer in our domain. Because it was "our" version of the User, even though the User came from a different domain.

phalt avatar Aug 20 '19 08:08 phalt

We finally solved it using a RelatedField that interacts with the UserInterface in the Outfits domain. This way we don't cross boundaries.

pedrogimenez avatar Aug 20 '19 11:08 pedrogimenez

@pedrogimenez very good idea. I like it! Can you share any code examples? I can promote this issue into a question for prosperity.

phalt avatar Aug 20 '19 13:08 phalt

I'm still not sure if this is the best approach but here's our implementation:

# com.outfits.serializers

from com.outfits.interfaces import UserInterface

class UserField(serializers.RelatedField):
    def to_representation(self, value):
        return UserInterface.get_user(id=value)

class OutfitSerializer(serializers.HyperlinkedModelSerializer):
    user = UserField(read_only=True)

    class Meta:
        model = Outfit
        fields = ['shortcode', 'user', 'url']
# com.outfits.interfaces

from com.users.apis import UserAPI

class UserInterface:

    @staticmethod
    def get_user(*, id: int) -> Dict:
        return UserAPI.get(id=id)

As I said above, the OutfitSerializer sends the get_user message to the UserInterface. This way, when you need to retrieve the user serialization, you do it through the ACL.

pedrogimenez avatar Aug 22 '19 06:08 pedrogimenez

I actually really like this pattern. I can foresee some performance issues if those interfaces are joining over network calls, but that would be a good opportunity to start keeping a shadow copy of the User in the Outfit serializer. But that only makes sense if you need a few attributes off of User. If you need the whole thing, it's a hard coupling to avoid.

phalt avatar Aug 22 '19 10:08 phalt