Возвращает URL-адрес в POST-методе при работе с base64ImageField и вложенными сериализаторами

#python #django #django-rest-framework #base64

#python #django #django-rest-framework #base64

Вопрос:

Прежде всего, я довольно новичок в Django и в равной степени новичок в серверной разработке, поэтому, пожалуйста, будьте проще со мной.

Я настраиваю серверную часть для мобильного приложения, в котором пользователь должен иметь возможность размещать рекламу с прикрепленным к ней изображением по Http-запросам. Прямо сейчас это работает так, как и должно быть, пользователь отправляет POST-запрос, содержащий всю необходимую информацию, и получает ответ, содержащий всю соответствующую информацию, за исключением изображения, которое равно null. Я знаю, что изображение успешно загружается на сервер, однако я не могу понять, как вернуть URL-адрес изображения обратно в ответ.

Ниже приведен мой код:

models.py

 class Advert(models.Model):

    owner=models.ForeignKey(User, on_delete=models.CASCADE, related_name="adverts")
    book_title = models.CharField(max_length=250)
    price = models.PositiveSmallIntegerField()
    created_at = models.DateTimeField(default=now())

    def __str__(self):
        return self.book_title   ' - '   self.contact_info


class AdvertImage(models.Model):
    advert = models.ForeignKey(Advert, on_delete=models.CASCADE, related_name="image", null=True)
    image = models.ImageField(upload_to = 'ad_images', null=True)

    def __str__(self):
        return self.image.name
 

Мои сериализаторы выглядят следующим образом:

serializers.py

 from rest_framework import serializers
from .models import Advert, AdvertImage
from drf_extra_fields.fields import Base64ImageField
from django.contrib.auth.models import User

class AdvertPostSerializer(serializers.ModelSerializer):
    image =  Base64ImageField(max_length=None, use_url=True, required=False)

    class Meta:
        model = Advert
        fields = (
        'id',
        'price',
        'book_title',
        'image')

    def create(self, validated_data):
        try:
            image_data = validated_data.pop('image')
        except:
            image_data = None
        advert = Advert.objects.create(**validated_data)
        if image_data is not None:
            image = AdvertImage.objects.create(advert=advert, image=image_data)

        return advert
 

И это мое мнение:

views.py

 class AdvertViewSet(viewsets.ModelViewSet):
    queryset = Advert.objects.all()
    permission_classes = (AllowAny,)

    def get_serializer_class(self):
        if self.action == 'create':
            return AdvertPostSerializer
        return AdvertSerializer

    @action(methods=['get'], detail=False)
    def newest(self,request):
        newest = self.get_queryset().order_by('created_at').last()
        serializer = self.get_serializer_class()(newest)
        return Response(serializer.data)

    def perform_create(self, serializer):
        return serializer.save(owner=self.request.user)
 

Чтобы проиллюстрировать, что происходит, вот запрос POST:

 POST http://localhost:8000/post-advert/ 
"Authorization: Token 980436128332ce3" 
book_title=my book 
price=1000
image=data:image/png;base64,iVBORHJvZmlsZSB0e// egAAAAASUVORK5CYII=
 

И это ответ:

 {
    "book_title": "my book",
    "id": 45,
    "image": null,
    "price": 1000,
}
 

Просмотр базы данных и отправка второго GET-запроса в другое представление показывает, что все загружено так, как должно, а внешние ключи и еще много чего работают так, как должны, просто мне очень сложно понять, как отправить URL-адрес изображения в ответ на успешный запрос.POST.

Ответ №1:

Хорошо, итак, мне удалось придумать (хакерский?) решение.

В serializers.py я помещаю свое поле base64 как read_only=True:

 class AdvertPostSerializer(serializers.ModelSerializer):
    image =  Base64ImageField(max_length=None, use_url=True, required=False, read_only=True)
    ...
 

Затем в моем views.py для моего AdvertViewSet я перезаписал свой метод create() как таковой:

 def create(self, request, format='json'):
        serializer = PostAdvertSerializer(data=request.data)
        if serializer.is_valid():
            advert = serializer.save()
            if advert:
                json = serializer.data
                advertImageURL = AdvertImage.objects.get(advert=advert).image.url
                json['img_link'] = request.build_absolute_uri(advertImageURL)
                return Response(json, status=status.HTTP_201_CREATED)
 

И это возвращает полный путь к моему изображению!

Ответ №2:

Поскольку ваш метод get_queryset() в

  newest = self.get_queryset().order_by('created_at').last()
 

возвращает объект рекламной модели:

 class AdvertViewSet(viewsets.ModelViewSet):
    queryset = Advert.objects.all()
 

у которых нет поля изображения. Затем вы создаете объект AdvertPostSerializer и инициализируете его с помощью новейшего набора запросов, который является набором запросов модели Advert без вашего изображения.

 serializer = self.get_serializer_class()(newest)
 

Вы можете каким-то образом получить объект AdvertImage внутри @newest action и попытаться добавить его в ответ, но думаю, что вы можете создать только одну модель Advert с вашим полем изображения и одним сериализатором для него, где вы определите Base64ImageField.

Комментарии:

1. Спасибо, что нашли время ответить. Даже если я прокомментирую «новейший» кодовый блок, ничего не изменится при выполнении POST-запросов к моим объявлениям. Объявления регистрируются как обычно, а изображение по-прежнему возвращает null. Я понимаю, что это связано с возвратом объявления, которое не содержит поля изображения, однако я не знаю, как заставить его вернуть URL.