How can i upload images when user is trying to create announcement. I need to upload images to my computer and assign them to the announcement that he is trying to create.
Models.py
class Announcement(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
photo = models.ManyToManyField(Photo, blank=True)
def nameFile(instance, filename):
return '/'.join(['images', str(instance.name), filename])
class Photo(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to=nameFile, blank=True, null=True)
Views.py
class AnnouncementCreate(CreateAPIView):
permission_classes = [IsAuthenticated]
queryset = models.Announcement.objects.all()
serializer_class = AnnouncementSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class PhotoViewSet(ListAPIView):
queryset = models.Photo.objects.all()
serializer_class = PhotoSerializer
def post(self, request, *args, **kwargs):
file = request.data.get('image')
image = models.Photo.objects.create(image=file)
return HttpResponse(json.dumps({'message': "Uploaded"}), status=200)
Serializers.py
class AnnouncementSerializer(serializers.ModelSerializer):
parameters = ParameterSerializer(many=True, required=False)
photo = PhotoSerializer(many=True, required=False)
class Meta:
model = Announcement
fields = ['id', 'name', 'address', 'date',
'price', 'description', 'author', 'parameters', 'photo']
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
fields = ('name', 'image')
The CreateModelMixin of the CreateAPIView calls the serializer listed. Therefore, you need to serialize and validate the image inside the AnnouncementSerializer using the PhotoSerializer. This can be done by overriding the create method of the AnnouncementSerializer:
class AnnouncementSerializer(serializers.ModelSerializer):
parameters = ParameterSerializer(many=True, required=False)
photo = PhotoSerializer(
many=True,
required=False,
read_only=True) # Set read_only to True
class Meta:
model = Announcement
fields = ['id', 'name', 'address', 'date',
'price', 'description', 'author', 'parameters', 'photo']
def create(self, validated_data):
"""
Serializes an image if it has been uploaded, then passes the image
back to the validated_data to finish creating the Announcement.
"""
image_data = self.context['request'].FILES
if image_data:
serializer = PhotoSerializer(data=image_data)
serializer.is_valid(raise_exception=True)
image = serializer.save()
validated_data['image'] = image
return super().create(validated_data)
That should be all you have to do.
Related
I would like when my PUT request is successful it returns me a response with all the fields in my PlantSerializer because currently the response returns me this:
{
"id":48,
"name":"Jar",
"width":"50",
"height":"50",
"exposure":"None",
"qr_code":"",
"garden":65,
"plant":[
7
]
}
But, I would like the response to return this instead:
{
"id":48,
"name":"Jar",
"width":"50",
"height":"50",
"exposure":"None",
"qr_code":"",
"garden":65,
"plant":[
"id":7,
"name":"Artichoke",
"image":null
]
}
How can I achieve this result?
Here is my serializers and my model class :
class Plot(models.Model):
name = models.CharField(max_length=50)
garden = models.ForeignKey('perma_gardens.Garden', on_delete=models.CASCADE)
width = models.CharField(max_length=50, blank=True, null=True)
height = models.CharField(max_length=50, blank=True, null=True)
plant = models.ManyToManyField('perma_plants.Plant', related_name='%(class)s_plant', blank=True)
# Here is my serializers :
class GardenSerializer(serializers.ModelSerializer):
class Meta:
model = Garden
fields = ('id', 'name',)
class PlantSerializer(serializers.ModelSerializer):
class Meta:
model = Plant
fields = ('id', 'name', 'image')
class ReadPlotSerializer(serializers.ModelSerializer):
garden = GardenSerializer(required=True)
plant = PlantSerializer(many=True)
id = serializers.IntegerField(read_only=True)
class Meta:
model = Plot
fields = '__all__'
read_only_fields = [fields]
class WritePlotSerializer(serializers.ModelSerializer):
class Meta:
model = Plot
fields = '__all__'
And here is my views :
class PlotViewSet(viewsets.ModelViewSet):
queryset = Plot.objects.all()
def create(self, request, *args, **kwargs):
serializer = WritePlotSerializer(data=request.data, many=isinstance(request.data, list))
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
def partial_update(self, request, *args, **kwargs):
instance = self.queryset.get(pk=kwargs.get('pk'))
serializer = WritePlotSerializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def get_serializer_class(self):
if self.action in ("list", "retrieve"):
return ReadPlotSerializer
return WritePlotSerializer
In the fuction partial_update you are utilizing the WritePlotSerializer, which only has the plant field implicitly through the fields=__all__ value. This is probably causing drf to use a PrimaryKeyRelatedField, and as such you don't get all the extra fields you defined in your PlantSerializer.
If I understand correctyl you want to use the WritePlotSerializer in the update but use the ReadPlotSerializer when returning the object. You probably should combine both of them in a single serializer by overriding the update method in order to support updating the nested Plant objects. Here is the related documentation.
Alternatively, if you don't want to update the Plants values you can use a slightly modified version of the ReadPlotSerializer in all calls:
class PlotSerializer(serializers.ModelSerializer):
garden = GardenSerializer(required=True, read_only=True)
plant = PlantSerializer(many=True, read_only=True)
id = serializers.IntegerField(read_only=True)
class Meta:
model = Plot
fields = '__all__'
I'm working on a DRF project to learn about ContentType models.
I created a post model and comment model(ContentType) and then added comments to the post. Everything was working fine until I added django-debug-tool and duplicated queries.
I have the following questions:
I've defined a method(children) and property(total_replies) on the comment model. Since total_replies just calling children method and count the size of queryset. Will it result in hitting the database two or more times in case I use the children method in some other methods or property?
If the database is hitting multiple times, what solution two improve performance?
After adding select_related the num of queries has been reduced drastically.
Before using select_related
After using select_related
Is it good to use select_related at all places where Foreignkey has been used?
Blog app
models.py
class Post(models.Model):
title = models.CharField(verbose_name=_("Post Title"), max_length=50)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='blog_posts')
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title
#property
def comments(self):
instance = self
#qs = Comment.objects.filter_by_instance(instance) #before
qs = Comment.objects.select_related('user').filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type = ContentType.objects.get_for_model(instance.__class__)
return content_type
serializers.py
class PostSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author',
'content', 'category', 'total_likes',
)
class PostDetailSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author', 'content',
'category', 'comments', 'total_likes'
)
def get_comments(self, obj):
request = self.context.get('request')
comments_qs = Comment.objects.filter_by_instance(obj)
comments = CommentSerializer(comments_qs, many=True, context={'request':request}).data
return comments
class PostListCreateAPIView(generics.ListCreateAPIView):
serializer_class = serializers.PostSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-list'
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class PostRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.PostDetailSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-detail'
permission_classes = [permissions.IsAuthenticatedOrReadOnly, account_permissions.IsStaffOrAuthorOrReadOnly]
def perform_update(self, serializer):
serializer.save(author=self.request.user)
Comment app
models.py
class CommentManager(models.Manager):
def all(self):
qs = super().filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
object_id = instance.id
qs = super().filter(content_type=content_type, object_id=object_id).select_related('user').filter(parent=None)
return qs
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comments')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
content = models.TextField()
objects = CommentManager()
def __str__(self):
if self.is_parent:
return f"comment {self.id} by {self.user}"
return f"reply {self.id} to comment {self.parent.id} by {self.user}"
def children(self):
return Comment.objects.select_related('user').filter(parent=self)
#property
def is_parent(self):
if self.parent is not None:
return False
return True
#property
def total_replies(self):
return self.children().count()
serializers.py
class CommentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content_type', 'object_id', 'parent', 'content', 'total_replies',)
class CommentChildSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content',)
class CommentDetailSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
replies = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = ('url', 'id', 'content_type', 'object_id', 'content', 'replies', 'total_replies',)
def get_replies(self, obj):
request = self.context.get('request')
if obj.is_parent:
return CommentChildSerializer(obj.children(), many=True, context={'request':request}).data
return None
views.py
class CommentListAPIView(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').order_by('-id')
name = 'comment-list'
serializer_class = serializers.CommentSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class CommentDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').all()
name = 'comment-detail'
serializer_class = serializers.CommentDetailSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Thanks in advance.
That's exactly what django docs says about select_related :
"Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries."
They describe select_related as something complex but good in term of transactional db cost.
I am a beginner in Django. Right now, I am working with the APIs. I am facing a problem. I can't view the string value one of the fields, called label, at http://127.0.0.1:8000/gameapi/. Instead of seeing "label_tag": "racing", I am seeing "label_tag": [2]. Here is the screenshot:
Here are my codes of serializers.py located inside gamreview folder.
from rest_framework import serializers
from .models import Game, Tags
# class TagSerializer(serializers.ModelSerializer):
# class Meta:
# model = Tags
# fields = ['label']
class GameSerializer(serializers.ModelSerializer):
# label_tag = TagSerializer(many=True)
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','label_tag']
def create(self, validated_data):
label_tag_data = validated_data.pop('label_tag')
game = Game.objects.create(**validated_data)
for tags_data in label_tag_data:
Tags.objects.create(game=game, **tags_data)
return Game.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.developer = validated_data.get('developer', instance.developer)
instance.platform = validated_data.get('platform', instance.platform)
# instance.tag = TagSerializer(read_only=True, many=True)
instance.save()
return instance
Here are my codes of models.py under gamreview folder:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Tags(models.Model):
label = models.CharField(max_length=20)
def __str__(self):
return self.label
class Game(models.Model):
title = models.CharField(max_length=100)
developer = models.CharField(max_length=100)
platform = models.CharField(max_length=50, default='null')
label_tag = models.ManyToManyField(Tags)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
review = models.CharField(max_length=1000)
date = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.game
Here are my codes of views.py under gamreview folder:
from django.views import generic
from .models import Game
from rest_framework import generics
from .serializers import GameSerializer
# Create your views here.
class GameListView(generic.ListView):
template_name = 'gamereview/gamelist.html'
context_object_name = 'all_games'
def get_queryset(self):
return Game.objects.all()
class ReviewView(generic.DetailView):
model = Game
template_name = 'gamereview/review.html'
# class GameApiView(generics.ListAPIView):
class GameApiView(generics.ListCreateAPIView):
queryset = Game.objects.all()
serializer_class = GameSerializer
class GameDetailApiView(generics.RetrieveUpdateDestroyAPIView):
queryset = Game.objects.all()
serializer_class = GameSerializer
Here are my codes of urls.py under gamreview folder:
from . import views
from django.urls import path
app_name = 'gamereview'
urlpatterns = [
path('gamereview/', views.GameListView.as_view(), name='gamelist'),
path('gamereview/<slug:slug>/', views.ReviewView.as_view(), name='review'),
path('gameapi/', views.GameApiView.as_view(), name='gamelistapi'),
path('gameapi/<int:pk>/', views.GameDetailApiView.as_view()),
]
I don't get any error while running the server. However, "label_tag": "racing"is not showing up.
How can I fix the issue?
in Game model change label_tag to
label_tag = models.ManyToManyField(Tags, related_name='tags')
this can be considered as tags is a field for a Game model
your serializer classes should be
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tags
fields = ['label']
class GameSerializer(serializers.ModelSerializer):
tags= TagSerializer(many=True)
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','tags']
you will get tags as a list of tags objects every tag have a label field with string value
'tags': [
{
'label': 'etc'
},
{
'label': 'etc2'
}
]
if you want tags like
'tags': [
'etc',
'etc'
]
this code will help you
```python
class GameSerializer(serializers.ModelSerializer):
label_tags = serializers.SerializerMethodField()
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','label_tags']
def get_label_tags(self, game):
new_tags = []
tags = game.label_tag.all()
for tag in tags:
new_tags.append(tag.label)
return new_tags
You can use the source argument
class GameSerializer(serializers.ModelSerializer):
label_tag = serializers.ReadOnlyField(source='label.label')
class Meta:
model = Game
fields = ['id', 'title', 'developer', 'platform','label_tag']
another way is to use a SerializerMethodField like this(it's an overkill in this case I believe)
class GameSerializer(serializers.ModelSerializer):
label_tag = serializers.SerializerMethodField()
class Meta:
model = Game
fields = ['id', 'title', 'developer', 'platform','label_tag']
def get_label_tag(self, obj):
return obj.label.label
check similar answers here DRF source argument
I'm a Django Rest Framework and Django newbie
i can use random data to make stages but i can't use serializer to add new stages.
My model and serializer
class Stage(models.Model):
class Meta:
db_table = 'stage'
stage_id = models.AutoField(primary_key=True)
stage_name = models.CharField(max_length=64, null=False)
company = models.ForeignKey(
Company,
db_column='id',
on_delete=models.CASCADE,
)
class StageSerializer(ModelSerializer):
stage_id = IntegerField(read_only=True)
class Meta:
model = Stage
fields = [
'stage_id',
'stage_name',
'company',
]
def update(self, instance, validated_data):
pass
def create(self, validated_data):
# create stages
stage = create_stage(**validated_data)
return stage
view.py
class StageListAPIView(APIView):
def post(self, request, company_id):
data = request.data.copy()
company = get_company_by_id(company_id)
data['company'] = company.pk
serializer = StageSerializer(data=data)
if not serializer.is_valid(raise_exception=True):
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
new_data = serializer.validated_data
serializer.save(company=company)
return Response(new_data, status=HTTP_200_OK)
request.data
<QueryDict: {'stage_name': ['kAkSdKq9Gt'], 'company': [6]}>
i will receive error:
TypeError: Object of type Company is not JSON serializable
i can't understand it and i don't know how to use serializer to save foreign key.
You need to serialize the Company instance before you can include it in your StageSerializer.
A simple example would be something like
class CompanySerializer(ModelSerializer):
class Meta:
model = Company
fields = '__all__'
And then to include that in your StageSerializer:
class StageSerializer(ModelSerializer):
stage_id = IntegerField(read_only=True)
company = CompanySerializer(source='company', read_only=True)
class Meta:
model = Stage
fields = [
'stage_id',
'stage_name',
'company',
]
In django rest framework, I am able to upload single file using danialfarid/ng-file-upload
views.py:
class PhotoViewSet(viewsets.ModelViewSet):
serializer_class = PhotoSerializer
parser_classes = (MultiPartParser, FormParser,)
queryset=Photo.objects.all()
def perform_create(self, serializer):
serializer.save(blogs=Blogs.objects.latest('created_at'),
image=self.request.data.get('image'))
serializers.py:
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
models.py:
class Photo(models.Model):
blogs = models.ForeignKey(Blogs, related_name='blogs_img')
image = models.ImageField(upload_to=content_file_name)
When I try to upload multiple file. I get in
chrome developer tools:
Request Payload
------WebKitFormBoundaryjOsYUxPLKB1N69Zn
Content-Disposition: form-data; name="image[0]"; filename="datacable.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryjOsYUxPLKB1N69Zn
Content-Disposition: form-data; name="image[1]"; filename="datacable2.jpg"
Content-Type: image/jpeg
Response:
{"image":["No file was submitted."]}
I don't know how to write serializer for uploading multiple file.
I manage to solve this issue and I hope it will help community
serializers.py:
class FileListSerializer ( serializers.Serializer ) :
image = serializers.ListField(
child=serializers.FileField( max_length=100000,
allow_empty_file=False,
use_url=False )
)
def create(self, validated_data):
blogs=Blogs.objects.latest('created_at')
image=validated_data.pop('image')
for img in image:
photo=Photo.objects.create(image=img,blogs=blogs,**validated_data)
return photo
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
read_only_fields = ("blogs",)
views.py:
class PhotoViewSet(viewsets.ModelViewSet):
serializer_class = FileListSerializer
parser_classes = (MultiPartParser, FormParser,)
queryset=Photo.objects.all()
I dont know it very well, but this is working...
This is for my viewset.
def perform_create(self, serializer):
obj = serializer.save()
for f in self.request.data.getlist('files'):
mf = MyFile.objects.create(file=f)
obj.files.add(mf)
Here is how you upload multiple files on blogs api:
models.py
class Blogs(models.Model):
...
class Photo(models.Model):
blogs = models.ForeignKey(Blogs, related_name='blogs_img')
image = models.ImageField(upload_to=content_file_name)
serializers.py
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
fields = ['blogs', 'image',]
class BlogsSerializer(serializers.ModelSerializer):
photos = serializers.SerializerMethodField()
def get_photos(self, obj):
photos = Photo.objects.filter(blogs=obj)
return PhotoSerializer(photos, many=True, read_only=False).data
class Meta:
model = Blogs
fields = [
...
'photos',
]
views.py
class BlogsViewSet(viewsets.ModelViewSet):
serializer_class = BlogsSerializer
queryset = Blogs.objects.all()
def create(self, request, *args, **kwargs):
instance_data = request.data
data = {key: value for key, value in instance_data.items()}
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
if request.FILES:
photos = dict((request.FILES).lists()).get('photos', None)
if photos:
for photo in photos:
photo_data = {}
photo_data["blogs"] = instance.pk
photo_data["image"] = photo
photo_serializer = PhotoSerializer(data=photo_data)
photo_serializer.is_valid(raise_exception=True)
photo_serializer.save()
return Response(serializer.data)
I have solved the issue with this solution
models.py:
class Product(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
class Images(models.Model):
product = model.ForeignKey('Product', related_name="images", on_delete=models.CASCADE)
image = models.ImageField(upload_to=upload_path)
serializers.py
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = models.Images
fields = '__all__'
views.py
class ImagesViewSet(ModelViewSet):
queryset = models.Images.objects.all()
serializer_class = serializers.ImageSerializer
# overwrite create method from the CreateModelMixin
def create(self, request, *args, **kwargs):
data = request.data
images = data.getlist('image')
# if no images call parent method it will return error
if not images:
return super().create(request, *args, **kwargs)
# verify only without creating the images
serializer_lst = []
for image in images:
data['image'] = image
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
serializer_lst.append(serializer)
serializers_data = [] # this is to collect data for all created images and return as list in the response
for serializer in serializer_lst:
self.perform_create(serializer)
serializers_data.append(serializer.data)
headers = self.get_success_headers(serializer.data)
return Response(serializers_data, status=status.HTTP_201_CREATED, headers=headers)
It took me a while to find out an effective solution to this,
I would like to share with you what finally worked for me.
I am using reactjs and DRF.
Here is my model :
class MediaFile(models.Model):
article = models.ForeignKey(Articles, on_delete=models.CASCADE, null=True)
caption = models.CharField(max_length=500, null=True, blank=True)
file = models.FileField('photo of article', upload_to=set_filename,
blank=True, null=True, default='')
added = models.DateTimeField(auto_now_add=True)
The views are standard viewsets.ModelViewSet
class MediaFilesViewSet(viewsets.ModelViewSet):
serializer_class = FileListSerializer
parser_classes = (parsers.MultiPartParser, parsers.FormParser,)
queryset=MediaFile.objects.all()
In ArticleSerializer I added:
def create(self, validated_data):
request = self.context.get('request')
user = request.user
instance = Articles.objects.create(**validated_data)
instance.publisher = user
instance.save()
images = request.FILES
if images:
try:
for f in images.getlist('mediafiles'):
instance.mediafile_set.get_or_create(file=f, caption=f.name)
instance.save()
except Exception as e:
print(e)
return instance
The FRONTEND structure:
postChangeImage = (event) =>{
this.setState({
is_images: true
});
let files = event.target.files;
const files_array = Object.values(files);
this.setState(
{imagefile: files}
);
console.log('IMAGES ARRAY', files_array);
files_array.map(value => {
const urls = URL.createObjectURL(value);
this.setState((prevState)=>(
{postfolio:[urls, ...prevState.postfolio]}
));
});
};
and POSTING:
for (let i=0; i< this.state.imagefile.length; i++) {
form_data.append(`mediafiles`, this.state.imagefile[i]);
}
The best answer to this question did not work for me, but Charles' suggestion worked very well. In my case, I needed to upload multiple files and assign them to a specific batch. Each batch is assigned to a particular user.
Below is more context using ReactJS to make the POST request, along with the serializers used and Postman window:
api.py
from convert_files.models import File
from rest_framework import viewsets, permissions
from rest_framework.parsers import MultiPartParser, JSONParser
from .serializers import BatchSerializer
class BatchViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticated
]
def perform_create(self, serializer):
obj = serializer.save(owner=self.request.user)
for f in self.request.data.getlist('files'):
mf = File.objects.create(office_file=f)
obj.files.add(mf)
parser_classes = (MultiPartParser, JSONParser, )
serializer_class = BatchSerializer
http_method_names = ['get','post','delete','put','patch', 'head']
def get_queryset(self):
return self.request.user.batches.all()
serializers.py
from rest_framework import serializers
from convert_files.models import File, Batch
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = '__all__'
class BatchSerializer(serializers.ModelSerializer):
files = FileSerializer(many=True, required = False)
class Meta:
model = Batch
fields = '__all__'
models.py
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from .extra import ContentTypeRestrictedFileField
def make_upload_path(instance, filename):
"""Generates upload path for FileField"""
return settings.OFFICE_OUTPUT_FILES_URL + "/%s" % (filename)
class Batch(models.Model):
name = models.CharField(max_length=100, blank=True)
description = models.TextField(blank=True)
date_posted = models.DateTimeField(default=datetime.datetime.now)
owner = models.ForeignKey(User, related_name="batches",
on_delete=models.CASCADE, null=True)
class File(models.Model):
name = models.CharField(max_length=100, blank=True)
office_file = ContentTypeRestrictedFileField(
upload_to = make_upload_path,
content_types = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel','application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
max_upload_size = 10485760,
)
files = models.ForeignKey(Batch, on_delete=models.CASCADE, null=True,
related_name='files', related_query_name='files')
FileUpload.js
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { addBatch } from '../actions/batches';
function FileUpload() {
const dispatch = useDispatch();
let formData = new FormData()
const onDrop = useCallback((acceptedFiles) => {
for (var i = 0; i < acceptedFiles.length; i++) {
formData.append("files", acceptedFiles[i], acceptedFiles[i].name)
}
dispatch(addBatch(formData));
})
...
Postman
Image of POST request in Postman for Multiple File Upload to DRF
Working with "dictionary (array) of images"
Ok, so today I tried Arindam's solution.. it worked perfectly, but after a while, I figgured out that my frontend (port 3000) makes a GET request to itself looking for an image that is not there, and not at the backend(port 8000).. (eg. GET http://localhost:3000/media/images/products/default.png - Returns 404: Not found).. What worked for me was to change the code around a bit and this is my solution..
in models.py
class Product(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
price = models.FloatField()
quantity = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False)
slug = models.SlugField(max_length=255, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
...
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
image = models.ImageField("Image", upload_to=upload_to, default='products/default.png')
in serializers.py
...
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ['id', 'image', 'product']
extra_kwargs = {
'product': {'required': False},
}
class ProductSerializer(serializers.ModelSerializer):
images = ProductImageSerializer(many=True, required=False)
class Meta:
model = Product
fields = ['id', 'title', 'description', 'images', 'price', 'quantity', 'active', 'slug', 'created_at', 'modified_at']
read_only_fields = ['slug']
#lookup_field = 'slug'
def create(self, validated_data):
product = Product.objects.create(**validated_data)
try:
# try to get and save images (if any)
images_data = dict((self.context['request'].FILES).lists()).get('images', None)
for image in images_data:
ProductImage.objects.create(product=product, image=image)
except:
# if no images are available - create using default image
ProductImage.objects.create(product=product)
return product
in views.py
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
permission_classes = [permissions.AllowAny]
serializer_class = ProductSerializer
parser_classes = [MultiPartParser, FormParser]
#lookup_field = 'slug'
edit: in settings.py
import os
...
BASE_DIR = Path(__file__).resolve().parent.parent
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
MEDIA_URL = '/media/'
I am posting this here to help someone (or me again in the future) with multiple files upload or multiple images upload as I spent 2 days looking up tutorials and answeres online to help me solve this issue... I may not be doing everything perfectly as I just recently started exploring Django REST Framework (and Python), but I hope it helps.