name 'InMemoryUploadedFile' is not defined in django model? - django

So basically I have a django model which has a filefield. I'd like to resize images that get submitted and I have this code here to do that
def save(self, *args, **kwargs):
img = Image.open(self.media)
output = BytesIO()
original_width, original_height = img.size
aspect_ratio = round(original_width / original_height)
desired_height = 100
desired_width = desired_height * aspect_ratio
img = img.resize((desired_width, desired_height))
img.save(output, format='JPEG', quality=90)
output.seek(0)
self.image = InMemoryUploadedFile(output, 'ImageField', "%s.jpg" % self.image.name.split('.')[0], 'image/jpeg', sys.getsizeof(output), None)
super(Post, self).save(*args, **kwargs)
The error im getting is "name 'InMemoryUploadedFile' is not defined". How do I fix this? Also a side question. The model also takes in videos. How would I resize a video? This is just a side question the InMemoryUploaded is the main question. Thank you!

did you import InMemoryUploadedFile?
please import it first
from django.core.files.uploadedfile import InMemoryUploadedFile

You don't need to use InMemoryUploadedFile to save a file. Also you get the error because you never import it. To import it from django.core.files import InMemoryUploadedFile. Instead of it you should better use ContentFile:
from django.core.files.base import ContentFile
...
def save(self, *args, **kwargs):
img = Image.open(self.media)
output = BytesIO()
original_width, original_height = img.size
aspect_ratio = round(original_width / original_height)
desired_height = 100
desired_width = desired_height * aspect_ratio
img = img.resize((desired_width, desired_height))
img.save(output, format='JPEG', quality=90)
output.seek(0)
self.image.save("%s.jpg" % self.image.name.split('.')[0], ContentFile(output), save=False)
super(Post, self).save(*args, **kwargs)

Related

Django custom save model creating duplicate files

I'm trying to get image uploads to also save as thumbnails, which works. The problem was when I did an update the custom method saved the thumbnail again in a different directory. so I modified my save function to look like this.
models.py
class Photo(models.Model):
title = models.CharField(max_length=64)
description = models.CharField(max_length=255)
created = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='photos/%Y%m')
thumbnail = models.ImageField(blank=True, upload_to='thumbnails/%Y%m')
submitter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
year = models.ForeignKey(Year, blank=True, on_delete=models.CASCADE)
people = TaggableManager(through=TaggedPeople, verbose_name='People')
tags = TaggableManager(through=TaggedGeneric, verbose_name='Tags')
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
def __str__(self):
return self.title
and my views
class PhotoCreateView(LoginRequiredMixin, CreateView):
model = Photo
fields = ['image', 'title', 'description', 'year', 'people', 'tags']
template_name = 'photoapp/create.html'
success_url = '/photo/?page=1'
extra_context = {'tags':GenericTag.objects.all().order_by('name'),'people':PeopleTag.objects.all().order_by('name'),}
def form_valid(self, form):
form.instance.thumbnail = self.request.FILES['image']
form.instance.submitter = self.request.user
return super().form_valid(form)
class PhotoUpdateView(LoginRequiredMixin, UpdateView):
template_name = 'photoapp/update.html'
model = Photo
fields = ['title', 'description', 'year', 'people', 'tags']
success_url = '/photo/?page=1'
So the CreateView now works fine, and I have stopped the duplicate thumbnail files, but the UpdateView does not work. How can I fix this?
I figured it out. I just had to add a save to the try section.
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
return super().save(*args, **kwargs)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
I had a similar complaint in Django-Wagtail, where saving image.save() programmatically duplicated on the file system the imported image. Do get around this I used Python Glob to get the path, and OS to remove the original image after the save function had been loaded. It worked well enough for Wagtail, I wanted to capture the issue for others - as there isn't much on the web about this. It's frustrating, doubles your disk space usage due to this functionality if you aren't careful! I don't like that it renames the files/moves them, but it is what it is.
from django.core.management.base import BaseCommand, CommandError
from wagtail.images.models import Image
from django.core.files.images import ImageFile
import os
from os import path
import glob
#import sqlite3
class Command(BaseCommand):
help = "just a thing to "
def handle(self, *args, **options):
target_path = "/home/inmyth/inmyth/media/images/"
my_images = []
if os.path.exists(target_path):
my_images = glob.glob(target_path + "*.png")
for mj_imgs in my_images:
print(mj_imgs)
image_file = ImageFile(open(mj_imgs, 'rb'), name=mj_imgs[:-4])
img_label = mj_imgs.rfind("/") + 1
image = Image(title=mj_imgs[img_label:-4], file=image_file)
image.save()
os.remove(mj_imgs)
pass

Django Change Image Format and Quality

Django project
Im trying to compress uploaded images and change their format
Currently it only compresses png images but when a jpg image is uploaded it does not change the format nor does it compress the image
please help sir
models.py
import sys
from PIL import Image
from io import BytesIO
from django.db import models
from django.contrib.auth.models import User
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.exceptions import ValidationError
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='images/userpfp/', blank=True)
def __str__(self):
return self.user.username
def compressImageMain(self,image):
if self.image:
def save(self, *args, **kwargs):
if not self.id:
self.image = self.compressImage(self.image)
super(UserProfile, self).save(*args, **kwargs)
def compressImage(self,image):
imageTemproary = Image.open(image)
outputIoStream = BytesIO()
basewidth = 520
wpercent = (basewidth/float(imageTemproary.size[0]))
hsize = int((float(imageTemproary.size[1])*float(wpercent)))
imageTemproaryResized = imageTemproary.resize( (basewidth,hsize) )
imageTemproaryResized.save(outputIoStream , format='PNG', quality=50)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.png" % image.name.split('.')[0], 'images/userpfp/png', sys.getsizeof(outputIoStream), None)
return image
else:
pass
I found the sollution
first I needed to add: .convert("RGB") when I opened the image
See: this post
img = Image.open(image).convert("RGB")
To maintain image quality over png and jpef image uploads:
See: this post
I compressed png to png and saved it as png
and compressed jpeg to jpeg and saved it as jpeg
(jpg and jpeg are the same)
import sys
from PIL import Image
from io import BytesIO
from django.core.files import File
from django import forms
from django.db import models
class Trade(models.Model):
image = models.ImageField(upload_to='images/trades/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
def save(self):
if not self.id:
self.image = self.compressImage(self.image)
super(Trade, self).save()
def compressImage(self,image):
img = Image.open(image).convert("RGB")
im_io = BytesIO()
if image.name.split('.')[1] == 'jpeg' or image.name.split('.')[1] == 'jpg':
img.save(im_io , format='jpeg', optimize=True, quality=55)
new_image = File(im_io, name="%s.jpeg" %image.name.split('.')[0],)
else:
img.save(im_io , format='png', optimize=True, quality=55)
new_image = File(im_io, name="%s.png" %image.name.split('.')[0],)
return new_image

How to compress images before uploading them to Amazon s3 using django storages and django rest framework?

I'm trying to compress the images before uploading them to the amazon s3 server, but I couldn't do it, I used 'PIL', to do it, but it didn't work
This is the code I used with the library 'PIL':
from io import BytesIO
from PIL import Image
from django.core.files import File
def compress(image):
im = Image.open(image)
im_io = BytesIO()
im.save(im_io,'PNG', quality=70)
new_image = File(im_io, name=image.name)
return new_image
class MyModel(models.Model):
file = models.FileField(blank=True, null=True, validators=[validateFileSize])
def save(self, *args, **kwargs):
new_image = compress(self.file)
self.file = new_image
super().save(*args, **kwargs)
I solved it using the following code
def compress(image):
im = Image.open(image)
# create a BytesIO object
im_io = BytesIO()
# save image to BytesIO object
#im = im.resize([500,500])
im = im.convert("RGB")
im = im.save(im_io,'JPEG', quality=70)
# create a django-friendly Files object
new_image = File(im_io, name=image.name)
return new_image
class Media(models.Model):
file = models.FileField(blank=True, null=True, validators=[validateFileSize])
def __str__(self):
return str(self.id)
def save(self, *args, **kwargs):
if self.file:
if self.file.size > (300 * 1024):
# call the compress function
new_image = compress(self.file)
# set self.image to new_image
self.file = new_image
# save
super().save(*args, **kwargs)

AttributeError : type object '_io.StringIO' has no attribute 'StringIO'

In my model I want to format the imagefield by overridding the save method
I have done this in my model
from PIL import Image as Img
from io import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
class Blog(models.Model):
Blog_image= models.ImageField(upload_to="...", blank=True)
def save(self, *args, **kwargs):
if self.Blog_image:
image = Img.open(StringIO.StringIO(self.Blog_image.read()))
image.thumbnail((900,300), Img.ANTIALIAS)
output = StringIO.StringIO()
image.save(output, format='JPEG', quality=150)
output.seek(0)
self.Blog_image = InMemoryUploadedFile(output,'ImageField', "%s.jpg" %self.Blog_image.name, 'image/jpeg', output.len, None)
super(Blog, self).save(*args, **kwargs)
But getting this Attribute error
AttributeError : type object '_io.StringIO' has no attribute 'StringIO'
Can anyone explain me why I am getting this error???
My python version is 3.6.4
My Django version is 2.0.7
output = StringIO.StringIO()
change it to:
output = StringIO()
you have already imported io.StringIO().
Got the solution
This works on Python 3.6.2 but I don't know where it was saved and from what folder it calls:
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys
def save(self, *args, **kwargs):
imageTemproary = Image.open(self.Blog_image)
outputIoStream = BytesIO()
imageTemproaryResized = imageTemproary.resize( (900,300) )
imageTemproaryResized.save(outputIoStream , format='JPEG', quality=150)
outputIoStream.seek(0)
self.Blog_image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" %self.Blog_image.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
super(Blog, self).save(*args, **kwargs)
Done it using BytesIO and it worked fine

How to compress uploaded image in django python

have been going through many tutorials on python on how to compress images, Have used PILLOW but its not working. I will be grateful if I can get a full documentation on how to use PILLOW for image compression inside my django views.py
i had used thumbnail() in pillow to compress images previously inside models.py The thumbnail() doesnt change the aspect ratio of image when resizing the image.
import sys
from django.db import models
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
def compressImage(uploaded_image):
image_temp = Image.open(uploaded_image)
outputIoStream = BytesIO()
image_temp.thumbnail( (1600,1600) )
image_temp.save(outputIoStream , format='JPEG', quality=100)
outputIoStream.seek(0)
uploaded_image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" % uploaded_image.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
return uploaded_image
class ABC(models.Model):
name = models.CharField(max_length=200)
image = models.ImageField(upload_to=/upload/path/here/, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.image = compressImage(self.image)
super(ABC, self).save(*args, **kwargs)