0

I am trying to be able to serialize and upload multiple images to associate with each post.

This is my models.py

from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save
from .utils import unique_slug_generator


class Painting(models.Model):
    user                        = models.ForeignKey(settings.AUTH_USER_MODEL, default="", on_delete=models.CASCADE)
    title                       = models.CharField(blank=False, null=False, default="", max_length=255)
    slug                        = models.SlugField(blank=True, null=True)
    style                       = models.CharField(blank=True, null=True, default="", max_length=255)       #need to figure out why there is problem when this is False
    description                 = models.TextField(blank=True, null=True, default="")
    size                        = models.CharField(blank=True, null=True, default="", max_length=255)
    artist                      = models.CharField(blank=True, null=True, default="", max_length=255)
    price                       = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=20)
    available                   = models.BooleanField(default=True)
    updated                     = models.DateTimeField(auto_now=True, auto_now_add=False)
    timestamp                   = models.DateTimeField(auto_now=False, auto_now_add=True)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ["-timestamp", "-updated"]

class PaintingPhotos(models.Model):
    title                       = models.ForeignKey(Painting, default="", on_delete=models.CASCADE)
    image                       = models.ImageField(upload_to='uploaded_paintings')


def pre_save_painting_receiver(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

pre_save.connect(pre_save_painting_receiver, sender=Painting)

my serializers.py

from django.contrib.auth import get_user_model, authenticate, login, logout
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone

from rest_framework import serializers

from .models import Painting, PaintingPhotos
User = get_user_model()


class UserPublicSerializer(serializers.ModelSerializer):
    username = serializers.CharField(required=False, allow_blank=True, read_only=True)
    class Meta:
        model = User
        fields = [
            'username',  
            'first_name',
            'last_name',
            ]


# # add PaintingImagesSerializer with the images model here
class PaintingPhotosSerializer(serializers.ModelSerializer):

    class Meta:
        model = PaintingPhotos
        fields =[
            'image'
        ]

#becareful here, if anyone submits a POST with an empty title, it will result in the empty slug, (which will mess up the url lookup since the title is the slug in this case)
#make title a required field in the actual interface, also remember to don't submit s POST with an empty title from the Django restframework directly
class PaintingSerializer(serializers.ModelSerializer):
    url             = serializers.HyperlinkedIdentityField(
                            view_name='paintings-api:detail',
                            read_only=True,
                            lookup_field='slug'
                            )
    user            = UserPublicSerializer(read_only=True)
    owner           = serializers.SerializerMethodField(read_only=True)
    image           = PaintingPhotosSerializer(many=True, read_only=False)

    class Meta:
        model = Painting
        fields = [
            'url',
            'user',
            'title',                    
            'style',                            
            'description',                
            'size',
            'artist',
            'price',                       
            'available',                                
            'updated',
            'timestamp',
            'owner',
            'slug',
            'image',
        ]

    def get_owner(self, obj):

        request = self.context['request']
        if request.user.is_authenticated:
            if obj.user == request.user:
                return True
        return False

my views.py

from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import generics, permissions, pagination, status

from .models import Painting
from .permissions import IsOwnerOrReadOnly
from .serializers import PaintingSerializer


class PaintingPageNumberPagination(pagination.PageNumberPagination):
    page_size = 5
    page_size_query_param = 'size'
    max_page_size = 20

    def get_paginated_response(self, data):
        author  = False
        user    = self.request.user
        if user.is_authenticated:
            author = True
        context = {
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'count': self.page.paginator.count,
            'author': author,
            'results': data,
        }
        return Response(context)


class PaintingDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset            = Painting.objects.all()
    serializer_class    = PaintingSerializer
    lookup_field        = 'slug'
    permission_classes  = [IsOwnerOrReadOnly]

class PaintingListCreateAPIView(generics.ListCreateAPIView):
    queryset            = Painting.objects.all()
    serializer_class    = PaintingSerializer
    permission_classes  = [permissions.IsAuthenticatedOrReadOnly]
    pagination_class    = PaintingPageNumberPagination


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

I am getting this error:

AttributeError: Got AttributeError when attempting to get a value for field image on serializer PaintingSerializer. The serializer field might be named incorrectly and not match any attribute or key on the Painting instance. Original exception text was: 'Painting' object has no attribute 'image'.

I am also not sure if I should create another app just to handle all the images.

Thanks so much in advance!

able-leopard
  • 115
  • 1
  • 14
  • Are you able to save the images to DB/storage backend using HTTP requests? Did you check the DB to ensure that? – JPG Sep 17 '19 at 05:10
  • @JPG I am using PostgreSQL do you think that's possible? What alternative would you suggest instead? – able-leopard Sep 17 '19 at 14:05
  • Files are uploaded to the file system and the path is stored in the DB. Check your MEDIA_ROOT setting. You also specified the folder 'uploaded_paintings', so the path will be whatever is in MEDIA_ROOT plus that. You can test if it is working by creating a PaintingPhotos object with an image in the Django admin. – bikemule Sep 17 '19 at 20:18
  • 1
    You should also name the class PaintingPhoto if you want to stick to Django standards. Django will pluralize it where appropriate to display. It seems minor, but it will save you bugs from typing things inconsistently in the long run. – bikemule Sep 17 '19 at 20:25
  • @bikemule thanks! I'll definitely change that. – able-leopard Sep 17 '19 at 22:24
  • 1
    I think this is a valid answer to your problem : https://stackoverflow.com/questions/48756249/django-rest-uploading-and-serializing-multiple-images. – Alexandre Oct 04 '19 at 12:01

2 Answers2

1

Your code looks similar enough to the docs here: https://www.django-rest-framework.org/api-guide/relations/#nested-relationships I can't see what exactly is wrong, but it could be that you haven't created a PaintingPhotos object so there is no model to serialize it. I mentioned in a comment that you can create this through the Django admin.

bikemule
  • 316
  • 1
  • 9
0

Hey guys I ended up finding the answer. This stackoverflow answer explains it really well: Multiple images per Model where I messed up was not adding the related_name argument to my photo in my PaintingPhotos model.

culix
  • 10,188
  • 6
  • 36
  • 52
able-leopard
  • 115
  • 1
  • 14
  • That link doesn't work. I figured it was something to do with that though. – bikemule Sep 20 '19 at 02:21
  • I think you may have meant to link here? https://stackoverflow.com/questions/537593/multiple-images-per-model I have updated this answer with the link (the previous one did not work) – culix Jan 03 '20 at 23:27