How to save the generated image from Django ImageKit to ImageField? - django

I generate the image using the code below:
source_file = open('/path/to/myimage.jpg', 'rb')
image_generator = Thumbnail(source=source_file)
result = image_generator.generate()
What is the proper method to save the generated "result" back to django ImageField? ie in a model
The generated result seems to be a _io.BytesIO object. And it seems I cannot directly save it to ImageField.
Any help would be appreciated

Assuming you have a SampleModel as below,
class SampleModel(models.Model):
image = models.ImageField(null=True)
then,ContentFile do the magic for you. Follow the snippet,
from django.core.files.base import ContentFile
source_file = open('/path/to/myimage.jpg', 'rb')
image_generator = Thumbnail(source=source_file)
result = image_generator.generate()
# additional snippet
django_file = ContentFile(result.getvalue())
sample = SampleModel.objects.create()
sample.image.save('sample_name.jpg', django_file)
sample.save()

Related

Django generate file and save it to model

My Django app generates a file. It takes img1.png and watermark.png and past them together and saves it again in the folder.
Everything works as expected.
This is the function:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
When working locally on my computer everything is good with specifying the path. However, in production it does not work anymore. I need to save the generatedfile.png directly on AWS S3.
For this reason I have create a simple model:
class pngUploadModel(models.Model):
auto_increment_id = models.AutoField(primary_key=True, default=True)
image = models.ImageField(null=True, blank=True, upload_to="png/")
I am able to upload images to this model using the admin interface. Everything still works as expected.
Now to my struggle.
I need to generate the image, and saving it "directly" to the model. Without saving it first to the path (because this will not work in production).
Approach:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
try:
filename = pngUploadModel.objects.create()
filename.image = img2
print('worked!')
except Exception as e:
print(f'this is the error message: {e}')
Output:
It creates an object in my model which I can see on my admin panel, but the model is empty, there is no image.
How can I save the generated image to my model, without having to save it first to my local directory. I was thinking to use something like a tempfile but I do not know if it is the right way.
If I'm correct, you want to get the generated file from the file path (f'{current_path}/media/pdf/generatedfile.png') and save it to your pngUploadModel.
An approach that I remember taking recently was to use the same prefix of the generated filename, setting that to be where the model instance image field points. For example:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
# the prefix string of the generated file -> f'{current_path}/media/pdf/generatedfile.png'
try:
genFile = pngUploadModel()
# Using the path/prefix of the generated file to be set to the image field
genFile.image = f'{current_path}/media/pdf/generatedfile.png'
genFile.save()
print('worked!')
except Exception as e:
print(f'this is the error message: {e}')
I used this answer as my guide then and it worked perfectly.
Another way is to save the generated file to the image field by passing a few arguments to the save() on the image/file field. Example:
from django.core.files.base import ContentFile # ensure you import
def generate():
prefix = f'{current_path}/media/pdf/generatedfile.png'
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(prefix)
# the prefix string of the generated file -> f'{current_path}/media/pdf/generatedfile.png'
# with open('/path/to/already/existing/file', 'rb') as f:
with open(prefix, 'rb') as f:
data = f.read()
genFile = pngUploadModel()
genFile.image.save('generatedfile.png', ContentFile(data))
genFile.save()
Ideally, that should work. You can also view other answers to this question as they might be helpful or can be used for future reference.

Django Reportlab getKeepWithNext

I am trying to add an image to a PDF generated in Reportlab. I am trying to access the image from a Django field, specifying the full path of the file.
When I run the below code I get: "Exception Value: getKeepWithNext".
Any help as to what I am doing wrong would be greatly appreciated.
def holding_pdf(self, course_slug, holding_slug):
buffer = io.BytesIO()
holding = HoldingDetail.objects.get(identifier=holding_slug)
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=A4,
title=f"Why the {holding.name} is in the portfolio.pdf")
elements = []
styles = getSampleStyleSheet()
elements.append(Paragraph(str(holding.logo.path), styles['Normal']))
elements.append(Image(holding.logo.path))
print(holding.logo.path)
doc.build(elements)
buffer.seek(0)
return FileResponse(buffer, as_attachment=False, filename=f"Why the {holding.name} is in the portfolio.pdf")
I think that you're using an incorrect Image class. Verify the python imports in your file, and sure you use the Image flowable class provides by reportlab.
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, SimpleDocTemplate, Image

Django - Converting a Binary stream to an Image

I am trying to obtain an image from a url and return it to the ModelAdmin to display it in a new column of the table.
I tried the following code in admin.py file:
def new_field(self, obj):
r = requests.get('https://abcd.com/image')
return r.content
The code is not giving me any error but it's returning a long binary string instead of the image itself.
How can I pass the image itself, or convert the binary content to an image?
You do not need download image if you wont only show it.
def new_field(self, obj):
url = 'https://abcd.com/image'
return '<img src="{}" />'.format(url)
new_field.allow_tags = True # it is important!!!
You can make use of a NamedTemporaryFile [GitHub] here. For example:
from django.core.files import File
from django.core.files.temp import NamedTemporaryFile
def store_image_from_source(self, obj):
img = NamedTemporaryFile()
r = requests.get('https://abcd.com/image')
img.write(r.content)
img.flush()
file = File(img)
obj.my_img_attr.save('filename.jpeg', file, save=True)
Here 'filename.jpeg' is thus te name of the file, as if you would have uploaded a file with that name with a ModelForm.

Append .png image to a pdf using reportlab in Django framework

I'm using Django and reportlab to generate a PDF report
I already can generate the pdf, but I Wanted to append a logo.png to it.
these were the lines I added in
views.py:
from reportlab.platypus import Image
logo = Image("/cdss/static/cdss/img/logo.png")
exam.append(logo)
But it isn't working, Am I exporting the Image() method wrong? Or is Path to the file wrong?
Hope you can help me, thanks ;)
This Worked For me....
def PrintImage(request,std_id):
response = HttpResponse(content_type='application/pdf')
doc = SimpleDocTemplate(response,topMargin=2)
doc.pagesize = landscape(A6)
elements = []
I = Image('http://demoschoolzen.educationzen.com/images/tia.png')
I.drawHeight = 0.7*inch
I.drawWidth = 0.7*inch
elements.append(I)
doc.build(elements)
return response
and Call it from your URLs

copy file from one model to another

I have 2 simple models:
class UploadImage(models.Model):
Image = models.ImageField(upload_to="temp/")
class RealImage(models.Model):
Image = models.ImageField(upload_to="real/")
And one form
class RealImageForm(ModelForm):
class Meta:
model = RealImage
I need to save file from UploadImage into RealImage. How could i do this.
Below code doesn't work
realform.Image=UploadImage.objects.get(id=image_id).Image
realform.save()
Tnx for help.
Inspired by Gerard's solution I came up with the following code:
from django.core.files.base import ContentFile
#...
class Example(models.Model):
file = models.FileField()
def duplicate(self):
"""
Duplicating this object including copying the file
"""
new_example = Example()
new_file = ContentFile(self.file.read())
new_file.name = self.file.name
new_example.file = new_file
new_example.save()
This will actually go as far as renaming the file by adding a "_1" to the filename so that both the original file and this new copy of the file can exist on disk at the same time.
Although this is late, but I would tackle this problem thus,
class UploadImage(models.Model):
Image = models.ImageField(upload_to="temp/")
# i need to delete the temp uploaded file from the file system when i delete this model
# from the database
def delete(self, using=None):
name = self.Image.name
# i ensure that the database record is deleted first before deleting the uploaded
# file from the filesystem.
super(UploadImage, self).delete(using)
self.Image.storage.delete(name)
class RealImage(models.Model):
Image = models.ImageField(upload_to="real/")
# in my view or where ever I want to do the copying i'll do this
import os
from django.core.files import File
uploaded_image = UploadImage.objects.get(id=image_id).Image
real_image = RealImage()
real_image.Image = File(uploaded_image, uploaded_image.name)
real_image.save()
uploaded_image.close()
uploaded_image.delete()
If I were using a model form to handle the process, i'll just do
# django model forms provides a reference to the associated model via the instance property
form.instance.Image = File(uploaded_image, os.path.basename(uploaded_image.path))
form.save()
uploaded_image.close()
uploaded_image.delete()
note that I ensure the uploaded_image file is closed because calling real_image.save() will open the file and read its content. That is handled by what ever storage system is used by the ImageField instance
Try doing that without using a form. Without knowing the exact error that you are getting, I can only speculate that the form's clean() method is raising an error because of a mismatch in the upload_to parameter.
Which brings me to my next point, if you are trying to copy the image from 'temp/' to 'real/', you will have to do a some file handling to move the file yourself (easier if you have PIL):
import Image
from django.conf import settings
u = UploadImage.objects.get(id=image_id)
im = Image.open(settings.MEDIA_ROOT + str(u.Image))
newpath = 'real/' + str(u.Image).split('/', 1)[1]
im.save(settings.MEDIA_ROOT + newpath)
r = RealImage.objects.create(Image=newpath)
Hope that helped...
I had the same problem and solved it like this, hope it helps anybody:
# models.py
class A(models.Model):
# other fields...
attachment = FileField(upload_to='a')
class B(models.Model):
# other fields...
attachment = FileField(upload_to='b')
# views.py or any file you need the code in
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.core.files.base import ContentFile
from main.models import A, B
obj1 = A.objects.get(pk=1)
# You and either copy the file to an existent object
obj2 = B.objects.get(pk=2)
# or create a new instance
obj2 = B(**some_params)
tmp_file = StringIO(obj1.attachment.read())
tmp_file = ContentFile(tmp_file.getvalue())
url = obj1.attachment.url.split('.')
ext = url.pop(-1)
name = url.pop(-1).split('/')[-1] # I have my files in a remote Storage, you can omit the split if it doesn't help you
tmp_file.name = '.'.join([name, ext])
obj2.attachment = tmp_file
# Remember to save you instance
obj2.save()
Update Gerard's Solution to handle it in a generic way:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.core.files.base import ContentFile
init_str = "src_obj." + src_field_name + ".read()"
file_name_str = "src_obj." + src_field_name + ".name"
try:
tmp_file = StringIO(eval(str(init_str)))
tmp_file = ContentFile(tmp_file.getvalue())
tmp_file.name = os.path.basename(eval(file_name_str))
except AttributeError:
tmp_file = None
if tmp_file:
try:
dest_obj.__dict__[dest_field_name] = tmp_file
dest_obj.save()
except KeyError:
pass
Variable's Used:
src_obj = source attachment object.
src_field_name = source attachment object's FileField Name.
dest_obj = destination attachment object.
dest_field_name = destination attachment object's FileField Name.