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.
Related
i am creating basic Coupon verification Django Rest framework APP, i just want to pass json parameter through Postman and get custom response if coupon code is valid or invalid.
Here is my models.py File:
from django.db import models
# Create your models here.
class Coupon(models.Model):
code = models.CharField(primary_key=True, max_length=50, unique=True)
def __str__(self):
return self.code
Here is my Serializers.py file:
from rest_framework import serializers
from .models import Coupon
class CouponSerializer(serializers.ModelSerializer):
class Meta:
model = Coupon
fields = '__all__'
I like to write views for given serializer and get custom message as mentioned above.
You can you UniqueValidator. Example code:
from rest_framework.validators import UniqueValidator
from rest_framework import serializers
from .models import Coupon
ERROR_MESSAGE_CODE_UNIQUE = 'This code already exists'
class CouponSerializer(serializers.ModelSerializer):
email = serializers.CharField(
max_length=50,
validators=[
UniqueValidator(
queryset=Coupon.objects.all(),
message=ERROR_MESSAGE_CODE_UNIQUE,
)])
class Meta:
model = Coupon
fields = '__all__'
You can use DRF APIView or mixins for this. The example for APIView:
class CouponValidationVie(APIView):
queryset = Coupon.objects
def get(self, request, code, format=None):
try:
coupon = Coupon.objects.get(code=code)
except Coupon.DoesNotExist:
return Response(
data={"message": "custom error message"}, status=status.HTTP_404_NOT_FOUND
)
serializer = CouponSerializer(coupon)
resp = {"data": serializer.data, "message": "custom success message"}
return Response(resp, status=status.HTTP_200_OK)
If Coupon record found it'll serialize record data and respond it with your custom "ok" message; else custom "failure" message returned.
Open POSTMAN and hit the verification url with raw request body like this {"code":"CouponCodeHere"}
For the verification url write the following view:
import json
from yourApp.models import Coupon
from django.http import HttpResponse
def check_coupon(request):
couponDict = json.loads(request.body)
try:
couponCode = couponDict["code"]
#You can apply strip i.e couponCode.strip() here to remove white spaces at the start and end.
if Coupon.objects.filter(code=couponCode).exists():
return HttpResponse("valid")
return HttpResponse("invalid")
except KeyError:
return HttpResponse("code does not exist in request body")
except Exception as e:
print(e) #For debugging purpose
return HttpResponse("Error")
I have the following :
I am working with DRF, based JWT token.
I want to associate an experiment with a USER, i.e when a post request is arriving I want to be able to save that post request with the Foreginkey it needed for the author by the user whom sent the request.
The POST request is always authenticated and never anonymous, i.e request.user is always exist ( I can see it when debugging)
I tried to add the following
def create(self, request, **kwargs):
request.data["author"] = request.user
serializer = ExperimentsSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return....
But is_valid return always False ( the only time ts was true, was when I took out the author from the ExperimentsSerializers fields....
will be happy for any leads....
my code attached below
Model.py:
class User(AbstractUser):
pass
def __str__(self):
return self.username
class Experiments(models.Model):
name = models.CharField(max_length=40)
time = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
View.py:
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
serializer_class = ExperimentsSerializers
queryset = Experiments.objects.all()
filterset_fields = '__all__'
permission_classes = (permissions.IsAuthenticated,)
serializers.py
class ExperimentsSerializers(serializers.ModelSerializer):
class Meta:
model = models.Experiments
fields = '__all__'
You can just pass additional data with save arguments:
def create(self, request, **kwargs):
serializer = ExperimentsSerializers(data=request.data)
if serializer.is_valid():
serializer.save(author=request.user)
Note that you may need to specify author field as read_only so it would not be required in request body:
class ExperimentsSerializers(serializers.ModelSerializer):
class Meta:
model = models.Experiments
fields = '__all__'
read_only_fields = ['author']
One more approach can be to use
HiddenField with default value set to CurrentUserDefault
This way that field will not be exposed at the same time current user will be accessible and other operations can be done on that user context.
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
Something like this:
class ExperimentsSerializers(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.Experiments
fields = '__all__'
Reference :
HiddenField - https://www.django-rest-framework.org/api-guide/fields/#hiddenfield
CurrentUserDefault - https://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
I have a model for articles which takes a field FileField which is supposed to be a markdown file for the user to load their article already. I expose the api with a ModelViewSet.
This is saved to my media folder. I could fetch the content from the client side by GETing it from the href of course but that would mean 2 requests to my server:
get article info (title, content- this is the md, date published, description, etc.. ).
get content from the link.
But i'm wondering if there's a way to tell django to just send the content of the file instead of the href when it responds to a requestion for the article item.
Here's my model and api:
# ---------------------- #
# src/articles/models.py #
# ---------------------- #
from os.path import splitext
from uuid import uuid4
from django.db import models
# Create your models here.
def hashFilename(instance, name):
ext = splitext(name)[1]
return "articles/{}{}".format(uuid4(), ext)
def hashImageFilename(instance, name):
ext = splitext(name)[1]
return "images/{}{}".format(uuid4(), ext)
class Article(models.Model):
title = models.CharField(("title"), max_length=100)
content = models.FileField("content", upload_to=hashFilename)
description = models.TextField(("description"), default='')
uploadDate = models.DateTimeField(("uploadDate"), auto_now=True)
lastModified = models.DateTimeField(("uploadDate"), auto_now=True)
publicationDate = models.DateField("publicationDate")
image = models.ImageField("image", upload_to=hashImageFilename)
def __str__(self):
return self.title
# ------------------------- #
# src/articles/api/views.py #
# ------------------------- #
from rest_framework.viewsets import ModelViewSet
from ..models import Article
from .serializers import ArticleSerializerFull, ArticleSerializerShort
class ArticlesViewSet(ModelViewSet):
queryset = Article.objects.all()
def get_serializer_class(self):
if self.action == 'list':
serializer = ArticleSerializerShort
else:
serializer = ArticleSerializerFull
return serializer
queryset = Article.objects.all()
Defining a serializers.SerializerMethodField--(DRF Doc) method will do the job.
class ArticleSerializer(serializers.ModelSerializer):
content = serializers.SerializerMethodField()
def get_content(self, article):
return article.content.file.read()
class Meta:
fields = '__all__'
model = Article
Alternatively, you could achieve the same by overriding the to_representation method of the serializer.
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Article
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['content'] = instance.content.file.read()
return rep
Update-1
From this comment, I hope you need a live markdown editor in Django Admin rather than a FileField.
So, Use any of these markdown packages to get a live view in the Django Admin. These packages are using models.TextField to store the markdown content. So that you could read the content from the field in anywhere just like any other model fields
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"}))
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.