2

I am trying to make an api endpoint for creating a post, the user can choose whether they want to upload a text(tweet) or an image alone or both, I have a validate method in my serializer for if no image nor text is sent, but it's raising the error on every try regardless if what I sent it. Am not entirely sure if this is a serializer class error or the script I wrote for sending the POST request

  • Here is the script:
import os
import requests
from PIL import Image

ENDPOINT = "http://0.0.0.0:8000/"

IMG = os.path.join(os.getcwd(), "img/img.jpg")


def send_request(method="GET", path="/", data=None, img_path=None):
    if data is None:
        data = {}
    if img_path is not None:
        with open(img_path, 'rb') as image:
            img_file = {"image": image}
            req = requests.request(method, ENDPOINT + path, data=data, files=img_file)
    else:
        req = requests.request(method, ENDPOINT + path, data=data)
    return req.text


res = send_request(method="POST", path="api/posts/create/", data={"user": 1, "content": ""}, img_path=IMG)

print(res)
  • Here's the model class:
from django.db import models
from django.conf import settings
import uuid
import os


class PostQuerySet(models.QuerySet):
    pass



class PostManager(models.Manager):
    def get_queryset(self):
        return PostQuerySet(self.model, using=self._db)


def upload_post_image(instance, filename):
    file_extension = filename.split('.')[-1]
    filename = f"{uuid.uuid4()}.{file_extension}"

    print(os.path.join())
    return os.path.join('post_image/', filename)



class Post(models.Model):
    user        = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content     = models.TextField(null=True, blank=True, max_length=255)
    image       = models.ImageField(null=True, upload_to=upload_post_image)

    def __str__(self):
        return str(self.content)[:50]

  • Here's the serializer class:
from rest_framework import serializers

from .models import Post


class PostSerializer(serializers.Serializer):

    class Meta:
        model = Post
        fields = ('user', 'content', 'image')

    def validate(self, data):
        content = data.get("content", None)
        image = data.get("image", None)
        if content == '':
            content = None

        if image is None and content is None:
            raise serializers.ValidationError("Content or an Image must be provided")
        return data
  • Here are my views:
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import generics, mixins, permissions, authentication
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import PostSerializer
from .models import Post


class PostManageAPIView(
    APIView,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    mixins.CreateModelMixin,
    mixins.ListModelMixin):

    def get(self, request, *args, **kwargs):
        print(self.request.GET)
        return Response({"message": "This is just a test"})


class PostDetailAPIView(generics.RetrieveAPIView):
    serializer_class = PostSerializer
    permission_classes = ()
    authentication_classes = ()
    queryset = Post.objects.all()

    def get_object(self, *args, **kwargs):
        kwargs = self.kwargs
        kw_id = kwargs.get('id')
        try:
            return Post.objects.get(id=kw_id)
        except ObjectDoesNotExist:
            return Response({})


class CreatePost(generics.CreateAPIView):
    serializer_class        = PostSerializer
    permission_classes      = ()
    authentication_classes  = ()

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

when I run the python script to send the request I always get the validation error saying content or an image must be provided

1 Answer 1

1

You have to check if there is a file via the view's request.FILES attribute. The file you posted would not be in the POST body.

If there is no file posted, the request.FILES attribute will be an empty list.

Here is how you can do it: (Note that in CreateAPIView's create method, serializer already has the request object in its context)

class PostSerializer(serializers.Serializer):

class Meta:
    model = Post
    fields = ('user', 'content', 'image')

def validate(self, data):
    content = data.get("content", None)
    request = self.context['request']
    # you dont need to set content explicitly to None

    if not request.FILES and not content:
        raise serializers.ValidationError("Content or an Image must be provided")
    return data
Sign up to request clarification or add additional context in comments.

4 Comments

now Django raises a "NotImplementedError" inside "/venv/lib/python3.8/site-packages/rest_framework/serializers.py"" saying: NotImplementedError: create()` must be implemented. `
instead of class serializers.Serializer, inherit from serializers.ModelSerializer, it automatically implements the create method for you I presume
It worked! Thank you very much Can you please tell me in short what is the real difference between serializers and model serializers? am new to Django so I still don't know when to use what exactly
Sure. In ModelSerializer, you can select which model fields to be serialized. The serializer already knows about your model and its fields, and the way to serialize them. You don't have to explicitly define serializer fields for each model field.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.