How to extend model on serializer level with djang

2019-02-07 06:32发布

My model looks like this:

class MenuItem(models.Model):
    name = models.CharField(max_length=500)
    components = models.ManyToManyField(Component, through=MenuItemComponent)

class Component(models.Model):
    name = models.CharField(max_length=500)

class MenuItemComponent(models.Model):
    menuItem = models.ForeignKey('MenuItem')
    component = models.ForeignKey(Component)
    isReplaceable = models.BooleanField()

What I'd like to do is expose a list of Components (NOT MenuItemComponents) in given MenuItem that would include the isReplaceable field. So far I have:

#views.py

class MenuItemComponentList(generics.ListAPIView):
    """
    Displays components for given MenuItem
    """
    model = MenuItemComponent
    serializer_class = MenuItemComponentSerializer

    def get_queryset(self):
        itemId = self.kwargs['itemId']
        return MenuItemComponent.objects.filter(menuItem__pk=itemId)



#serializers.py

class MenuItemComponentSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = MenuItemComponent

Which exposes a list of MenuItemComponents and forces clients to make multiple calls in order to retrieve all Components. Exposing Components list with additional data from isReplaceable field would solve the problem.

EDIT
In the end I'd like to get a list that lists Component elements but the elements are exteded with isReplaceable field from MenuItemComponent model:

{
    "count": 2, 
        "next": null, 
        "previous": null, 
        "results": [
        {
            "url": "http://localhost:8000/api/component/1/", 
            "name": "component 1", 
            "isReplaceable": true
        }, 
        {
            "url": "http://localhost:8000/api/component/2/",  
            "name": "component 2",
            "isReplaceable": false
        }
    ]
}

1条回答
乱世女痞
2楼-- · 2019-02-07 07:17

First, create a view that will return the MenuItemComponent instances that you're interested in.

class ListComponents(generics.ListAPIView):
    serializer_class = MenuItemComponentSerializer

    def get_queryset(self):
        """
        Override .get_queryset() to filter the items returned by the list.
        """
        menuitem = self.kwargs['menuitem']
        return MenuItemComponent.objects.filter(menuItem=menuitem)

Then you need to create a serializer to give you the representation you want. Your example is a bit more interesting/involved than the typical case, so it'd look something like this...

class MenuItemComponentSerializer(serializers.Serializer):
    url = ComponentURLField(source='component')
    name = Field(source='component.name')
    isReplaceable = Field()

The fields 'name' and 'isReplaceable' can simply use the default read-only Field class.

There's no field that quite meets your 'url' case here, so we'll create a custom field for that:

class ComponentURLField(serializers.Field):
    def to_native(self, obj):
        """
        Return a URL, given a component instance, 'obj'.
        """

        # Something like this...
        request = self.context['request']
        return reverse('component-detail', kwargs=kwargs, request=request)

I think that should all be about right.

That's for a read-only serialization - if you wanted a writable serialization you'd need to look into overriding the restore_object method on the serializer, and using WritableField, or something along those lines.

查看更多
登录 后发表回答