So i have been trying to upload a file to a method using DRF with no luck so far.
I was able to upload to a ModelViewSet using (FormParser, MultiPartParser,) with no problems, but i really need to use it in something like this http://localhost:8000/api/v1/women/{pk}/upload_avatar/ where i want to first filter the woman by id and upload to her avatar (which is a foreign key to a multimedia model). I tried using a nested resource library with no luck.
So far i have in my modelviewset:
class WomenNativePassportViewSet(viewsets.ModelViewSet):
queryset = Women.objects.all()
serializer_class = WomenNativePassportSerializer
authentication_classes = (NoAuthentication,)
permission_classes = (AllowAny,)
parser_classes = (FormParser, MultiPartParser,)
#detail_route(
methods=['post', 'put', 'patch', 'get'], permission_classes=[AllowAny],
authentication_classes=[NoAuthentication], serializer_class=MultimediaSerializer,
parser_classes=(FormParser, MultiPartParser,)
)
def upload_avatar(self, request, pk=None, *args, **kwargs):
if 'POST' in request._method or 'PATCH' in request._method:
# Write code to save the file??
else:
multimedia = Multimedia.objects.filter(user_profiles_avatares__pk=pk)
page = self.paginate_queryset(multimedia)
serializer = self.get_pagination_serializer(page)
return Response(serializer.data)
My models:
class Women(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
avatar = models.ForeignKey(
'core.Multimedia', blank=True, null=True,
related_name='user_profiles_avatares'
)
class Multimedia(models.Model):
file = models.FileField(upload_to=upload_to, null=True, blank=True)
thumbnail = models.FileField(upload_to=upload_to, null=True, blank=True)
Basically i want to know if this is the right path i am taking, and if yes how can i properly save the uploaded file in the model??
Here is some code of what i did to overcome this problem. Although Kevin Brown answer probably works, i find my code a little "easier" approach:
#detail_route(
methods=['post', 'put', 'patch', 'get'], permission_classes=[AllowAny],
authentication_classes=[NoAuthentication], serializer_class=MultimediaSerializer,
parser_classes=(FormParser, MultiPartParser,)
)
def upload_avatar(self, request, pk=None):
# Because we are using nested resources this was the only way i found to
# upload a file. Maybe there is a better way
if request.method in ['PATCH', 'POST']:
avatar = request.FILES.get('avatar')
if not avatar:
return Response(status=404)
try:
woman = WomenNativePassport.objects.get(pk=pk)
except WomenNativePassport.DoesNotExist:
return Response(status=404)
else:
request.FILES['thumbnail'] = request.FILES['avatar']
serializer = AvatarSerializer(
data=request.DATA, files=request.FILES
)
if serializer.is_valid():
woman.avatar.thumbnail.save(str(avatar), File(avatar))
return Response(status=204)
else:
return Response(status=404)
else:
multimedia = Multimedia.objects.filter(user_profiles_avatares__pk=pk)
page = self.paginate_queryset(multimedia)
serializer = self.get_pagination_serializer(page)
return Response(serializer.data)
# serializer
class AvatarSerializer(serializers.Serializer):
thumbnail = serializers.ImageField()
Any uploaded files should be available in request.FILES, a dictionary keyed by the field that they was used when uploading. Once you have the file, it's a matter of handling it similar to any other uploaded file in Django.
If you can, I would use a second serializer that wraps the Multimedia model so the image validation and saving can be done automatically through Django REST Framework. There is an ImageField that will automatically validate the image by Pillow which you can use on the serializer.
Related
Hello everyone reading this post. I got such issue.
So, first of all I have such models layout
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
description = models.TextField(max_length=4000, null=True)
liked_profiles = models.ManyToManyField('self', related_name='likes')
disliked_profiles = models.ManyToManyField('self', related_name='dislikes')
class Image(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='images', max_length=6)
path = models.ImageField(upload_to='profile_images')
So, I want to create a drf endpoint that will receive some kind of image, create it, and link to the profile model. But I really not sure how to implement this(I want to make it with django viewsets).
The main goal is not to provide another viewset class (like ImageViewSet), but to make it part of ProfileViewSet. So now I have such viewset (contains a method to update the description)
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
permission_classes = (IsAuthenticated, )
#action(detail=True, methods=['PUT'])
def set_description(self, request, pk=None):
profile = self.get_object()
serializer = DescriptionSerializer(data=request.data)
if serializer.is_valid():
profile.description = request.data['description']
profile.save()
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
I want to add something like "add_image" method to it.
How can I implement it and is it actually possible(otherwise you can help me to implement it with another viewset)?
I will be extremely grateful for your help
You can do sth similar to your set_description:
#action(
detail=True,
methods=["post"],
serializer_class=ImageSerializer, # write your custom serializer, override save() method and save images from self.context["request"].FILES.items()
)
def create_image(self, request, pk=None):
instance = self.get_object()
serializer = self.get_serializer(instance, data=self.request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
I'm trying to save an image from a url in the post request.
Here is what I have so far, the error is while passing the file to the serializer.
#models.py
class Picture(models.Model):
picture = models.ImageField(
upload_to=upload_location_picture, max_length=255
)
owner = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='owned_pictures')
#views.py
class PlanPostPictureByUrl(APIView):
'''
Class to post dislike to plan
'''
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, format=None):
img_url = request.data["picture"]
name = urlparse(img_url).path.split('/')[-1]
response = requests.get(img_url)
if response.status_code == 200:
serializer = PlanPicturesSerializer(
data={"picture":ContentFile(response.content)})
if serializer.is_valid():
serializer.save(owner=self.request.user)
return Response(status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
#serializers.py
class PlanPicturesSerializer(serializers.ModelSerializer):
class Meta:
model = Picture
fields = ['picture']
This is the error I am getting from the serializer:
{'picture': [ErrorDetail(string='No filename could be determined.', code='no_name')]}
I have something like this in my serializers create and update methods.
from django.core.files.uploadedfile import UploadedFile
url = validated_data['newIconFromURL']
# Remove url from the submitted data
validated_data['newIconFromURL'] = ''
# Download data from url (requires `requests` module. Can also be done with urllib)
response = requests.get(validated_data['newIconFromURL'])
# Set icon field (ImageField) to binary file
validated_data['icon'] = UploadedFile(BytesIO(response.content), name='icon')
Note that the file name does not need to be unique, as Django will append random characters to the actual filename to make it unique.
I have a Django Rest Framework + Android App where the user can upload some video files to my local Django development server. I wanted to show the videos as GIF files via a RecyclerView. I thought that creating the GIF files on server side (Django) would be more efficient than creating them on the client side (device).
So I want to make a GIF file from an uploaded video before storing the video file.
How can I do that in Django ?
Here is my models.py:
class Video(models.Model):
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=100, blank=True)
video = models.FileField(upload_to='Videos/',
blank=True)
class Meta:
ordering = ('created', )
This is how my views.py looks like:
# used for displaying & creating video items
class VideoList(generics.ListCreateAPIView):
queryset = Video.objects.all()
serializer_class = VideoSerializer
parser_classes = (MultiPartParser, )
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
# the only part we change
return Response({'videos': serializer.data})
And this is the urls.py with the url the app uses to upload the video file:
urlpatterns = [
...
path('upload/', views.VideoList.as_view())
]
The upload of videos from my app works, but I don't know what to do to create also a GIF file version of the uploaded video.
Which is the best way to achieve that & how can I embed it into my Django-related files such as views.py or models.py?
I would very thankful for any hint or any advice. Hope someone can help...
from moviepy.editor import *
clip = (VideoFileClip("ENTER THE FILE PATH HERE"))
clip.write_gif("output.gif")
A simpler idea, make a model in your models.py file probably and then convert the video to gif using that model. That model could look similar to this and once it converts it, maybe use the output where you want to.
I am creating a page for adding products to the web shop by the user, and I am having troubles with uploading images.
I have a form with an <input type="file"> and after selecting the files, the files (images) should be temporarily uploaded to the server. After the upload is complete, image preview should be displayed. During the upload, loading spinner GIF with client-side progress indication should be displayed too, along with the option to cancel the upload. Each uploaded image should have an option to be deleted, once uploaded.
Because of all this and other reasons, I'm not using a ModelForm, but creating everything manually.
So, my plan is to attach an onchange listener to that input, and upon files selection, a POST XMLHttpRequest would be sent from JavaScript, which would call a view that would save the images into some kind of table for temporarily uploaded images, and the ID of the row would be something like request.session.session_key, but a little bit different. My issue with this approach is generating that session key, which should be unique and it should last as long as the page is opened. So, refreshing the page would mean generating a new key. request.session.session_key doesn't suit my needs as it remains the same throughout the session. When entire form is submitted, I plan to somehow connect those temporarily uploaded images with my main product instance, and delete them from the table for temporary images.
What do you think of this solution, and if it is viable, how to generate the key?
#EDIT:
models.py:
class Product(models.Model):
class Meta:
abstract = True
category = models.ForeignKey('Category', on_delete=models.CASCADE, blank=False)
title = models.CharField(max_length=200)
price = models.DecimalField(decimal_places=2, max_digits=10)
free_shipping = models.BooleanField()
stock = models.PositiveSmallIntegerField()
image = models.ImageField(upload_to=get_image_path, blank=True, null=True)
date_added = models.DateTimeField(auto_now=True, null=True)
class Book(Product):
year = models.PositiveSmallIntegerField()
publisher = models.CharField(max_length=50)
author = models.CharField(max_length=50)
language = models.CharField(max_length=20)
isbn = models.CharField(max_length=50)
class Shoes(Product):
size = models.PositiveSmallIntegerField()
colour = models.CharField(max_length=20)
I have something similar done using Angular
products.ts
onFileChanged(event) {
let reader = new FileReader();
let me = this;
if (event.target.files.length > 0) {
const file = event.target.files[0];
reader.readAsDataURL(file);
reader.onload = function () {
me.imagen = reader.result;
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
}
onSubmit(f){
// f is my form
let postdata=f.value;
postdata.imagen={'imagen': this.imagen};
postdata.usuario=+localStorage.getItem("id_usuario");
postdata.categoria=+postdata.categoria;
postdata.precio=+postdata.precio;
postdata.donacion=+postdata.donacion;
console.log(postdata);
this.articuloServicio.postNuevoArticulo(postdata)
.subscribe(res=>{
this.router.navigateByUrl('/mis_productos')
},(error=>{
alert('Error, verifique la informacion ingresada');
}));
}
models.py
class Image(models.Model):
image=models.ImageField(upload_to='imagenes/',blank=False, null=False)
articule=models.ForeignKey(Articule,on_delete=models.CASCADE,
related_name="images",
null=False, blank=False)
Some images can be for the same product
serializers.py
class ArticuloSerializer(serializers.ModelSerializer):
imagen = ImagenCreateSerializer(write_only=True)
class Meta:
model = Articulo
fields = ('id', 'categoria','usuario','nombre','precio','donacion','descrip', 'imagen')
def create(self, validated_data):
imagenes = validated_data.pop('imagen')
articulo = Articulo(**validated_data)
articulo.save()
for imagen in imagenes.values():
Imagen.objects.create(articulo=articulo, imagen=imagen)
return articulo
views.py
class ArticulosList(generics.ListCreateAPIView):
queryset = Articulo.objects.all()
serializer_class = ArticuloSerializer
def list(self, request, *args, **kwargs):
queryset = Articulo.objects.all()
serializer = ArticuloSerializerDetail(queryset, context={'request': request}, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs):
serializer = ArticuloSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
In products.ts: In onFileChanged, that manage the file upload, I use Filereader to get the image an is assigned to a
variable in my Angular component. In onSubmit, I get the data from my form f and the image, all the data is located in an object
to be sended in my service in a POST request.
In models.py I share my Image model, that is attached to an Articule, that is other model in my schema. The image field is a
ImageField from Django.
In serializers.py I share my ArticuleSerializer that serialize the data for my Articule get and post views. As you can see
there is a create function, in that you can define how the data is managed when is a post request.
In views.py, I define my Articule view for get and post, the one for post is the create function. To call that view
in your urls.py assign it as
path("endpoint/", ArticuleList.as_view({"get": "list", "post": "create"}))
I was previously using APIViews such as the following:
views.py
class AllProgramsApi(APIView):
def get(self, request):
user = self.request.user
userprograms = Program.objects.filter(user=user)
serializer = ProgramSerializer(userprograms, many=True)
return Response(serializer.data)
here's my model:
class Program(models.Model):
program_name = models.CharField(max_length=50)
program_description = models.CharField(max_length=250)
cycles = models.ManyToManyField(Cycle)
is_favourite = models.BooleanField(default="False")
user = models.ForeignKey(User, on_delete=models.CASCADE)
def get_absolute_url(self):
return reverse('programs:program', kwargs={'pk': self.pk})
def __str__(self):
return self.program_name
Now I've discovered ModelViewSet, which looks very convenient, but I can't seem to be able to filter for the user as I was previously doing in the APIView.
my attempt at views.py with ModelViewSet is the following and it works but I get all the content and not just the content related to a single user.
class AllProgramsApi(ModelViewSet):
serializer_class = ProgramSerializer
queryset = Program.objects.all()
How can I tweak the ModelViewSet so that it displays only the content related to the user who sends the request? What is the best method?
Thanks.
You can use get queryset method,if you know more refer the doc Filtering against the current user
class AllProgramsApi(ModelViewSet):
serializer_class = ProgramSerializer
queryset = Program.objects.all()
def get_queryset(self):
queryset = self.queryset
query_set = queryset.filter(user=self.request.user)
return query_set
there are permission_classes in django you can add permissions as per your requirements or you can create custom permissions
you would get better idea from django permission
or you can create your queryset by defining get_queryset method.