1

Right now I am learning to create REST API with Django, but am struggling to implement the Update method. My tests always fail and I am not sure if it is because of the serializer or something else. Would appreciate some quick feedback for my code!

class Company(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)

class JobPost(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='guest_posts')
    heading = models.CharField(max_length=100, default="")

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('name')

class JobPostSerializer(serializers.ModelSerializer):
    company = CompanySerializer(read_only=True, required=False)

    class Meta:
        model = GuestPost
        fields = ('id', 'heading', 'company')

    def create(self, validated_data):
        request = self.context.get('request')
        company = Company.objects.get(title=request.data.get('company_name'))
        return GuestPost.objects.create(owner=request.user,
                                        heading=validated_data.get('heading'),
                                        company=company)

    def update(self, instance, validated_data):
        instance.heading = validated_data.pop('heading')
        instance.save()
        return instance

class JobPostViewSet(viewsets.ViewSet,
                     viewsets.generics.ListCreateAPIView,
                     viewsets.mixins.UpdateModelMixin,
                     viewsets.mixins.RetrieveModelMixin):
    permission_classes = (JobPostPermissions,)
    serializer_class = JobPostSerializer
    queryset = JobPost.objects.all()

I then test this code, all my tests for the other operations (create, get, list) work but I get an error 415 when testing the update using the following method:

class Test(TestCase):

    def setUp(self):
        self.user = User.objects.create(username="elon")
        self.factory = RequestFactory()
        self.request_url = '/api/job-post/'
        self.boring_company = Company.objects.create(owner=self.user, title="Boring Company")
        self.job_post = JobPost.objects.create(company=self.boring_company, heading="Looking for boring person")
        self.sample_uuid = self.job_post.id

    def test_update_post(self):
        data = {
            'id': self.sample_uuid,
            'heading': 'New Heading',
            'description': 'new description',
        }
        request = self.factory.put(self.request_url, data=data)
        view = GuestPostViewSet.as_view({'put': 'update'})
        force_authenticate(request, user=self.user)
        response = view(request, pk=self.sample_uuid)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['heading'], 'New Heading')

Thanks in advance!

Hamidreza
  • 1,465
  • 12
  • 31
Borzi
  • 537
  • 1
  • 5
  • 21

2 Answers2

1

I stumbled upon the answer after a second look at the Django Rest docs. As others have pointed out, it was the test that was the problem. Here is the official example of what you need to do for update requests:

from django.test.client import encode_multipart, RequestFactory

factory = RequestFactory()
data = {'title': 'remember to email dave'}
content = encode_multipart('BoUnDaRyStRiNg', data)
content_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg'
request = factory.put('/notes/547/', content, content_type=content_type)
Borzi
  • 537
  • 1
  • 5
  • 21
0

The 415 response refers to Unsupported Media Type, so it indicates that the payload you are sending to server is not valid to server. As far as I know, when you are sending a put request, you must put the whole model in your request! (for more information see this answer) And as I can see here, you are missing the company in your request payload (data dictionary in your code) However I don't know what is inside GuestPostViewSet but I can guess it would use JobPostSerializer as its serializer which have 'company' in the fields tuple of its Meta class!

For updating a model without sending the whole attributes of it, you can use patch request instead. So change request type and view in your test class:

request = self.factory.patch(self.request_url, data=data)

...

view = GuestPostViewSet.as_view({'patch': 'update'})
Hamidreza
  • 1,465
  • 12
  • 31