images do not match pil, django model - python

I'm trying to create a QR Code in my Django model which creates itself whenever a new record is saved. The problem is that when testing in my dev environment everything is fine but in production I get an error on the specified line below saying: ValueError, images do not match.
from django.core.files import File
import qrcode
from io import BytesIO
from PIL import Image
from datetime import date
# this is the save method of the django model class, I just left out the other methods
def save(self, *args, **kwargs):
img = qrcode.make(self.account_id)
canvas = Image.new('RGB', (400, 400), 'white')
canvas.paste(img) # Error occurs on this line
buffer = BytesIO()
canvas.save(buffer, 'PNG')
canvas.close()
fname = f'pdf-qr-code-{self.account_id}.png'
self.menu_qr_code.save(fname, File(buffer), save=False)
super(PDFMenu, self).save(*args, **kwargs)
I don't know if maybe the problem is with my configurations on my ubuntu server since that's the place where the problem takes place but maybe a new way of creating a qr code within this custom save method is necessary.
Thanks in advance for the help.

Related

Django choose in what location images are loaded in model's save method

Here's my save method for model with field image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True):
def save(self, *args, **kwargs):
if self.image:
self.image = compress(self.image)
if self.second_image:
self.second_image = compress(self.second_image)
if self.third_image:
self.third_image = compress(self.third_image)
if self.fourth_image:
self.fourth_image = compress(self.fourth_image)
super().save(*args, **kwargs)
It works fine and compresses all images but changes image's directory every time when I click save in django admin. It makes all images' paths be like:
before edited and saved: products/2020/11/05/img.jpeg
after: products/2020/11/05/products/2020/11/05/img.jpeg
click save one more time: products/2020/11/05/products/2020/11/05/products/2020/11/05/img.jpeg
And then I get this error:
SuspiciousFileOperation at /admin/shop/product/6/change/
Storage can not find an available filename for "products\2020\11\05\products\2020\11\05\products\2020\...... .jpeg".
Please make sure that the corresponding file field allows sufficient "max_length".
How can I fix this problem? I think I need to choose location where saved images would be stored. Django doesn't let me use absolute path in upload_to field so I have no idea.
compress func is:
from io import BytesIO
from PIL import Image
from django.core.files import File
def compress(image):
im = Image.open(image)
if im.mode != 'RGB':
im = im.convert('RGB')
im_io = BytesIO()
im.save(im_io, 'JPEG', quality=70)
compressed_image = File(im_io, name=image.name)
return compressed_image
The problem is that the filename is updated everytime. At each step, you should make sure that the filename is only a filename. Something like this:
import os
if self.image:
self.image = compress(self.image)
self.image.name = os.path.basename(self.image.name)
I don't know exactly what is your compress function, but maybe you could also check that it doesn't do something weird with the filename.

Django converting tiff to jpeg with pillow

I'm working with Django in Python 3, and I have a model with an ImageField and I'm trying to override the .save() to do tiff to jpeg conversion:
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
class MyModel(models.Model):
image = models.ImageField(upload_to=upload_image_to, editable=True, null=True, blank=True)
def save(self, *args, **kwargs):
pil_image_obj = Image.open(self.image)
new_image_io = BytesIO()
rgb_pil_image_obj = pil_image_obj.convert("RGB")
rgb_pil_image_obj.save(new_image_io, quality=90, 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().save(*args, **kwargs)
However, this leads to:
tempfile.tif: Cannot read TIFF header.
*** OSError: -2
If I try the save experiment but only opening the TIFF file from disk instead of feeding PIL.Image with the Django InMemoryUploadedFile, then everything works absolutely fine and the tiff is converted to a jpeg.
Also pil_image_obj.verify() doesn't throw any exceptions.
I'm using Pillow==5.3.0
What could be the issue? And is there any other way to do such a conversion?
You need to pass the path of the image file to Image.open(). Currently you're passing the field itself.
pil_image_obj = Image.open(self.image.path) # Pass the path

How to get image from the web and save it to a imageField using the url

I'm trying to get a image from the web and save it to the imageField using the images url.
The below code spits out a error ('JpegImageFile' object has no attribute '_committed').
from PIL import Image
import urllib.request
import io
if form.is_valid():
instance = form.save(commit=False)
URL = 'http://www.image.jpg'
with urllib.request.urlopen(URL) as url:
file = io.BytesIO(url.read())
img = Image.open(file)
instance.image = img
instance.image has to be of type django.core.files.images.ImageFile I am testing my models with this code. Maybe it points you to the correct direction :
# This gets me a temp Image for testing. In your case it would be a real file
def get_test_image_file(tmpdir=None):
file = tempfile.NamedTemporaryFile(suffix='.png', dir=tmpdir)
return ImageFile(file, name=file.name)
and the for the model test I do :
def test_addimage_create(self):
adimage = AdImage.objects.create(advertiser=self.advertiser,
picture=get_test_image_file(tmpdir=MEDIA_ROOT))
self.assertIsInstance(adimage, AdImage)
so in your case instead of instance.image = img. img has to be an Object of Type ImageFile which then can be assigned to the form.instance. Btw. you should assign the values to the instance before save()

Saving BytesIO to Django ImageField

I have a web scraper that I want to download an image of the page it's scraping and save it as a "screenshot" ImageField in a Django model. I am using this code:
def save_screenshot(source,screenshot):
box = (0, 0, 1200, 600)
im = Image.open(io.BytesIO(screenshot))
region = im.crop(box)
tempfile_io = io.BytesIO()
region.save(tempfile_io, 'JPEG', optimize=True, quality=70)
source.screenshot.save(source.slug_name+"-screenshot",ContentFile(tempfile_io.getvalue()),save=True)
It saves the screenshot to the /media/news_source_screenshots/ directory but doesn't save it to the model. The model field is defined as:
screenshot = models.ImageField(upload_to='news_source_screenshots',blank=True,null=True)
What am I missing?
So it turns out the above code works great! The issue was that I was calling the above method using a piece of code like this:
source = NewsSource.objects.get(name=name)
html,screenshot = get_url(source.url)
save_screenshot(source,screenshot)
source.save()
So the save_sceenshot method worked but then the work it had done was overwritten by my source.save() call. Go figure!

how to get full path from binary image in odoo

I want to use PIL function Image.open(), but it only works if I pass the image path as an argument. I have to find a way to get this image path. I'm using widget='image' and odoo 8
The images are stored in database, base64 encoded. You will have to save them to a file yourself.
import tempfile
import base64
import os
from PIL import Image
from openerp import models, fields, api
from openerp.exceptions import UserError
class MyModel(models.Model):
[...]
image = fields.Binary()
#api.multi
def open_image(self):
self.ensure_one()
if not self.image:
raise UserError("no image on this record")
# decode the base64 encoded data
data = base64.decodestring(self.image)
# create a temporary file, and save the image
fobj = tempfile.NamedTemporaryFile(delete=False)
fname = fobj.name
fobj.write(data)
fobj.close()
# open the image with PIL
try:
image = Image.open(fname)
# do stuff here
finally:
# delete the file when done
os.unlink(fname)

Categories