Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend model on serializer level with django-rest-framework

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
        }
    ]
}
like image 891
Jacek Chmielewski Avatar asked Nov 20 '12 10:11

Jacek Chmielewski


People also ask

How do you pass extra context data to Serializers in Django REST framework?

In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.

What is the difference between serializer and model serializer?

The ModelSerializer class is the same as a regular Serializer class, except that: It will automatically generate a set of fields for you, based on the model. It will automatically generate validators for the serializer, such as unique_together validators. It includes simple default implementations of .

Do we need Serializers in Django REST framework?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

What is depth in Django serializer?

DRF allows you to expose a REST framework based on your django data models with very little code needed. To get a basic REST API that supports Create-Read-Update-Delete operations on your data you have to do two things: You specify a serializer that tells drf how to go from your model to a JSON representation.


1 Answers

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.

like image 72
Tom Christie Avatar answered Sep 22 '22 08:09

Tom Christie