Django - how to write test for DRF ImageField - django

I have the following serializer:
from rest_framework.serializers import Serializer, ImageField
class MySerializer(Serializer):
avatar = ImageField()
how can I write a unit test for it?
I used the Django TestCase, but it raises an error.
from django.test import TestCase
class MySerializerTest(TestCase):
def setUp(self):
self.data = {}
...
def test_image(self):
import tempfile
self.data['avatar'] = tempfile.NamedTemporaryFile(suffix=".jpg").file
r_data = json.dumps(self.data)
j_data = json.loads(r_data)
serializer = MySerializer(data=j_data)
if not serializer.is_valid():
import pprint
pprint.pprint(serializer.errors)
self.assertEqual(serializer.is_valid(), True)
but it raises the following error:
TypeError: Object of type 'BufferedRandom' is not JSON serializable
What's my mistake? how can I write a test for image field?

I suggest to use SimpleUploadedFile class from django and create and image using Pillow package. See the example below.
from PIL import Image
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.utils.six import BytesIO
class MySerializerTest(TestCase):
...
def test_image(self):
image = BytesIO()
Image.new('RGB', (100, 100)).save(image, 'JPEG')
image.seek(0)
self.data['avatar'] = SimpleUploadedFile('image.jpg', image.getvalue())
serializer = MySerializer(data=self.data)
self.assertEqual(serializer.is_valid(), True)

Usually when you upload a file you would use the multipart request format, and the view would convert the image into an InMemoryUploadedFile object, and that gets passed into your serializer
So to fix your tests I'd recommend trying:
from PIL import Image
from tempfile import NamedTemporaryFile
from django.conf.files.uploadedfile import InMemoryUploadedFile
...
def test_image(self):
image = Image.new("RGB", (100, 100))
with NamedTemporaryFile(suffix=".png", mode="w+b") as tmp_file:
image.save(tmp_file, format="png")
tmp_file.seek(0)
byio = BytesIO(temp_file.read())
inm_file = InMemoryUploadedFile(
file=byio,
field_name="avatar",
name="testImage.png",
content_type="image/png",
size=byio.getbuffer().nbytes,
charset=None,
)
self.data['avatar'] = inm_file
serializer = MySerializer(data=self.data)
if not serializer.is_valid():
import pprint
pprint.pprint(serializer.errors)
self.assertEqual(serializer.is_valid(), True)
What this is doing is:
Create an image in memory using PIL.Image
Create a NamedTemporaryFile to store the Image data
Take the NamedTemporaryFile and read into a InMemoryUploadedFile
Pass this InMemoryUploadedFile into the serializer

Related

How to view API data within a GET method that's created using POST method in Django (without a model)?

I have created a DRF API that allows me to submit an image using the POST method via POSTMAN. The image is not stored in the model. After it's submission, I want to view the image's name in the browser using the Django Rest Framework. After reading sources in the net, I found out that people used the GET method in order to view all the data in a model. However, I don't have a model (don't require it for now) so how can I implement this requirement?
The result should be something like this:
{
"image_name": <"name_of_the_image_stored">
}
This is what I had done so far:
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import ImgSerializer
from rest_framework import status
from rest_framework.parsers import FileUploadParser
class ImageAPI(APIView):
parser_classes = (FileUploadParser,)
def post(self, request):
#key is 'image' when uploading in POSTMAN
file = self.request.data
data = file['image']
if data:
uploaded_file = data
fs = FileSystemStorage(location=settings.PRIVATE_STORAGE_ROOT)
filename = fs.save(uploaded_file.name, uploaded_file)
data = [{"image_name": filename}]
serializer = ImgSerializer(data, many = True).data
return Response(serializer, status = 200)
else:
return Response("Please upload an image", status = 400)
def get(self, request):
#What should I do here in order to view the submitted image above?
serializers.py:
from rest_framework import serializers
class ImgSerializer(serializers.Serializer):
image_name = serializers.CharField()
urls.py:
from upload.views import ImageAPI
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
path("api/", ImageAPI.as_view(), name = "api"),
]
urlpatterns = format_suffix_patterns(urlpatterns)
First of all parser_classes should be an attribute of ImageAPI class, as I can see you've created it as a local variable, which won't do what you want. According to docs the request.data property should be a dictionary with a single key file containing the uploaded file. And regarding viewing the saved image, here you can find some ways to do that.
This is an example:
...
def get(self, request, *args, **kwargs):
# here I assume you've sent the name of the image using query params,
# but there are other better ways to do that
image_name = request.GET.get('image')
# here you should read the file from your storage
image_file = <read_image_file_by_given_name(image_name)>
return HttpResponse(image_file, content_type='image/png')

Django: Model Queryset to Pandas to Django Rest Framework

I am trying to accomplish the following workflow in my Django project:
Query my database
Convert the returned queryset to a pandas dataframe in order to perform some calculations & filtering
Pass the final dataframe to Django REST API Framework
if I understand correctly, I have to use django-pandas for Step 2. and Django REST Pandas for Step 3.
I installed both and read the documentaton, but I have no clue how to make it work.
What I have achieved to far is to set up my model, views, serializes and urls to have the original queryset rendered via the Django Rest Framework.
If anyone could give me a hint on how to integrate pandas in this workflow, it would be highly appreciated.
my models.py file
from django.db import models
class Fund(models.Model):
name = models.CharField(max_length=100)
commitment_size = models.IntegerField(blank=True, null=True)
commitment_date = models.DateField(blank=True, null=True)
def __str__(self):
return self.name
my views.py file
from rest_framework import generics
from rest_framework.views import APIView
from pages.models import Fund
from .serializers import FundSerializer
class FundAPIView(generics.ListAPIView):
queryset = Fund.objects.all()
serializer_class = FundSerializer
my serializers.oy file
from rest_framework import serializers
from pages.models import Fund
class FundSerializer(serializers.ModelSerializer):
class Meta:
model = Fund
fields = ('name', 'commitment_size', 'commitment_date')
So i figured it out. First, you need to install django-pandas and django rest framework (but no need to install django REST pandas)
models.py and serializers.py files stay the same as above, but the views file is different
from rest_framework.views import APIView
from rest_framework.response import Response
from django_pandas.io import read_frame # Import django_pandas.io read frame
from pages.models import Fund
from .serializers import FundSerializer
class TestData(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, format=None):
data = Fund.objects.all() # Perform database query
df = read_frame(data) # Transform queryset into pandas dataframe
df['testcolumn'] = "testdata" # Perform some Pandas Operation with dataframe
return Response(df) # Return the result in JSON via Django REST Framework

'<' not supported between instances of 'Applicant' and 'Applicant'

I have a model that I want to use for predictions which I have loaded using pickle and I have a form created in using django. But when a user submits the form I want it to be in store it in a csv format in a variable so I can perform Xgboost prediction on every form the user fills and after it outputs the prediction. COuld it be its not getting any input. New to this
from django.db import models
from django import forms
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
type_loan=(("Cash loan","Cash loan"),
("Revolving loan","Revolving Loan"))
Gender=(("Male","Male"),
("Female","Female"))
Yes_NO=(("YES","YES"),("NO","NO"))
status=(("Single","Single"),
("Married","Married"),
("Widow","Widow"),
("Seprated","Divorce"))
Highest_Education=(("Secondary","Secondary"),
("Incomplete Higher","Incomplete Higher"),
("Lower Secondary","Lower Secondary"),
("Academic Degree","Academic Degree"))
Income_type=(("Working","Working Class"),
("State Servant","Civil Servant"),
("Commercial Associate","Commercial Associate"),
("Pensioner","Pensioner"),
("Student","Student"),
("Businessman","Business Owner"))
class Applicant(models.Model):
name=models.CharField(default="Jon Samuel",max_length=50,null="True")
Birth_date=models.DateField(default="2018-03-12",blank=False, null=True)
Status=models.CharField(choices=status,max_length=50)
Children=models.IntegerField(default=0,validators=[MinValueValidator(0),MaxValueValidator(17)])
Highest_Education=models.CharField(choices=Highest_Education,max_length=50)
Gender=models.CharField(choices=Gender, max_length=50)
loan_type=models.CharField(choices=type_loan, max_length=50)
own_a_car=models.CharField(choices=Yes_NO,max_length=50)
own_a_house=models.CharField(choices=Yes_NO,max_length=50)
def __str__(self):
return self.name
views.py
from django.shortcuts import render
from .models import Applicant
from .forms import Applicant_form
from django.views.generic import ListView, CreateView, UpdateView
from django.core.cache import cache
import xgboost as xgb
import pickle
from sklearn.preprocessing import LabelEncoder
class CreateMyModelView(CreateView):
model = Applicant
form_class = Applicant_form
template_name = 'loan/index.html'
success_url = '/loan/results'
context_object_name = 'name'
class MyModelListView(ListView):
template_name = 'loan/result.html'
context_object_name = 'Results'
def get_queryset(self):
queryset=Applicant.objects.all()
with open('model/newloan_model','rb') as f:
clf=pickle.load(f)
le=LabelEncoder()
le.fit(queryset)
queryset=le.transform(queryset)
d_test = xgb.DMatrix(queryset)
predict=clf.predict(d_test)
return (predict)
One way to accomplish this is to use a Django package called django-import-export. This way you can export every object inside a model to a csv file.
https://django-import-export.readthedocs.io/en/latest/

Django 1.11: Flip image horizontally before saving into a Django model

I'm doing this application where I take a user's image and then flip it horizontally using ImageOps from the Pillow library. To do so I made a model like above:
from django.db import models
class ImageClient(models.Model):
image = models.ImageField(null=False, blank=False)
I made a form using ImageField with a html form with enctype="multipart/form-data" and in my views I did the following:
from django.shortcuts import render, redirect
from .forms import ImageForm
from .models import ImageClient
from PIL import Image, ImageOps
def new(request):
"""
Returns mirror image from client.
"""
if request.method == 'POST':
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
image = Image.open(form.cleaned_data['image'])
image = ImageOps.mirror(image)
form_image = ImageClient(image=image)
form_image.save()
return redirect('img:detail', pk=form_image.id)
else:
form = ImageForm()
return render(request, 'img/new_egami.html', {'form':form})
....
As you see, when a check if the form is valid, I open the form's image and flip it horizontally (using ImageOps.mirror()) then I save it. But I always getting this error 'Image' object has no attribute '_committed'. I know the Image object is from Pillow, but I do not understand this error. Can someone explain and/or solve this error?
The error is raised because the image is a PIL Image object, whereas Django is expecting and requires its own File object. You could save the Image object to an absolute file path and then refer to it, but there are more efficient ways here and here. Here is an adaptation of #madzohan's answer in the latter link for your image operation:
# models.py
from io import BytesIO
from django.core.files.base import ContentFile
from PIL import Image, ImageOps
class ImageClient(models.Model):
image = models.ImageField(null=False, blank=False, upload_to="image/path/")
def save(self, *args, **kwargs):
pil_image_obj = Image.open(self.image)
new_image = ImageOps.mirror(pil_image_obj)
new_image_io = BytesIO()
new_image.save(new_image_io, format='JPEG')
temp_name = self.image.name
self.image.delete(save=False)
self.image.save(
temp_name,
content=ContentFile(new_image_io.getvalue()),
save=False
)
super(ImageClient, self).save(*args, **kwargs)
and views.py:
...
if form.is_valid():
new_image = form.save()
return redirect('img:detail', pk=new_image.id)

Displaying a raw image using CBVs in Django

I want to do some image manipulation, but before I do I want to render the image. This is what I've tried, but it does not work. The page is blank, no errors, why?
class ImgThumbnail(DetailView):
queryset = Images.objects.all()
def render_to_response(self, context, **response_kwargs):
from PIL import Image
import requests
from io import BytesIO
response = requests.get('http://example.com/media/images/{}.jpg'.format(self.pk))
img = Image.open(BytesIO(response.content))
return HttpResponse(img, content_type='image/jpg')
You should use StreamingHttpResponse. In a way like:
(dynamic content type and content length as bonus)
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
from wsgiref.util import FileWrapper
from django.http import HttpResponse, StreamingHttpResponse
class ImgThumbnail(DetailView):
queryset = Images.objects.all()
def get(self, request, *args, **kwargs):
r = requests.get('http://example.com/media/images/{}.jpg'.format(self.pk))
if not r.status_code == 200:
return HttpResponse('', status=r.status_code)
wrapper = FileWrapper(StringIO(r.content))
response = StreamingHttpResponse(wrapper, content_type=r.headers.get('Content-Type'))
response['Content-Length'] = r.headers.get('Content-Length')
return response
DetailView doesn't have a render_to_response method, so there is no point in defining your own.
You're not using any of the functionality of DetailView here anyway. You should inherit from the base View class, and put your code in the get method.