smithy
smithy

Reputation: 417

Django REST framework and creating two table entries from a POST form

I want to create entries in two tables (Log and Post) using the DRF Browseable API POST form.

The example below is contrived, but it outlines what I am trying to do.

class Post(models.Model):
    info = models.CharField()

class Log(TimeStampedModel):
    ip = models.GenericIPAddressField(('IP Address'))
    name = models.CharField()
    data = models.ForeignKey(Post)                                       

I want to use the browseable API to submit a form to create a Log entry. Here are the serializers:

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('info',)

class LogSerializer(serializers.ModelSerializer):                          
    data = serializers.Field()

    class Meta:
        model = Log
        fields = ('ip', 'name', 'data')

The problem with the above is that serializer.Field is read only so does not show up on the POST form. If I change it to CharField it shows up, but then I get an error because an instance of a Post is expected not just a field of the Post object.

Here are my views:

class LogMixin(object):
    queryset = Log.objects.all()                                           
    serializer_class = LogSerializer



class LogList(LogMixin, ListCreateAPIView):
    pass


class LogDetail(LogMixin, RetrieveUpdateDestroyAPIView):
    pass

What's the correct way of doing this?

Upvotes: 2

Views: 4943

Answers (2)

DragonFire
DragonFire

Reputation: 4082

This is a hacky way and the best way to use the nested serializer as stated above. But just to show another way I am posting it here.

    # Add New Item
    @api_view(('POST',))
    def add_new_item(request):
    
    
        request.data['areaname_name'] = request.data['item_areaname']
        request.data['buildingname_name'] = request.data['item_buildingname']
    
        item_serializer = TblItemSerializer(data=request.data)
        area_name_serializer = TblAreanameSerializer(data=request.data)
        building_name_serializer = TblBuildingnameSerializer(data=request.data)

        response = []
        
        if item_serializer.is_valid()  & area_name_serializer.is_valid() & building_name_serializer.is_valid():
    
            # Save To Item  Table
            item_serializer.save()
    
            # Save Unique Values Into Areaname and Buildingname Tables
            area_name_serializer.save()
            building_name_serializer.save()
    
            return Response(item_serializer.data, status=status.HTTP_201_CREATED)
        else:
            response.append(item_serializer.errors)
            response.append(area_name_serializer.errors)
            response.append(building_name_serializer.errors)

            return Response(response, status=status.HTTP_400_BAD_REQUEST)

In the error response you could also use (depending on how you want to handle on client side)

merge = {**item_serializer.errors, **area_name_serializer.errors, **building_name_serializer.errors}
return Response(merge, status=status.HTTP_400_BAD_REQUEST)

Upvotes: 0

robert
robert

Reputation: 819

From what I can tell you want to create a nested Log object. There are 2 ways of doing this:

  1. Send 2 POST Requests, One to create the Post, and the other to create the Log contained the received HTTP 200 data from the API.
  2. (Django and best way) Send the data all in one POST and parse it server side. Django Rest Framework takes care of this for you.

I have changed your code so that it should work. Source

class LogSerializer(serializers.ModelSerializer):                          
   class Meta:
        model = Log
        fields = ('ip', 'name')


class PostSerializer(serializers.ModelSerializer):
    log = LogSerializer()
    class Meta:
        model = Post
        fields = ('info', 'log')

views.py

import generics

class PostCreateAPIView(generics.CreateAPIView):
    model = Post
    serializer_class = PostSerializer

Then you can send a POST Request containing 'info', 'ip', and 'name'.

Upvotes: 3

Related Questions