The Python Oracle

Save a generated PIL image into an ImageField in django

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Techno Bleepage Open

--

Chapters
00:00 Save A Generated Pil Image Into An Imagefield In Django
00:53 Answer 1 Score 5
01:35 Accepted Answer Score 13
02:10 Thank you

--

Full question
https://stackoverflow.com/questions/4906...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #django #python3x #pythonimaginglibrary

#avk47



ACCEPTED ANSWER

Score 13


You can use a BytesIO to save the Pillow file to an in-memory blob. Then create a File object and pass that to your model instance ImageField's save method.

from io import BytesIO
from django.core.files import File

canvas = Image.new('RGB', (total_width, total_height), 'white')
...
blob = BytesIO()
canvas.save(blob, 'JPEG')  
self.qrcode_file.save('ticket-filename.jpg', File(blob), save=False) 

Check out the django documentation for the File object. https://docs.djangoproject.com/en/stable/ref/files/file/#the-file-object

You have to use save=False, since the default save=True means that the parent model's save method would be called after the image is saved. You don't want recursion here, since you would typically end up in an infinite loop.




ANSWER 2

Score 5


Change your code and use Django File as below:

from django.core.files import File


class Ticket(models.Model):
    booked_at = models.DateTimeField(default=timezone.now)
    qrcode_file = models.ImageField(upload_to='qrcode', blank=True, null=True)
    bought = models.BooleanField(default=False)

    def save(self, *args, **kwargs):
        if self.bought:
            ...
            ...
            qrcode_img = qrcode.make('some data')
            canvas = Image.new('RGB', (total_width, total_height), 'white')
            draw = ImageDraw.Draw(canvas)
            position = (left, top)
            canvas.paste(qrcode_img, position)

            canvas.save('path/of/dest.png', 'PNG')
            destination_file = open('path/of/dest.png', 'rb')
            self.qrcode_file.save('dest.png', File(destination_file), save=False)
            destination_file.close()

            self.booked_at = timezone.now()
            super(Ticket, self).save(*args, **kwargs)
            canvas.close()
            qrcode_img.close()
        else:
            self.booked_at = timezone.now()
            super(Ticket, self).save(*args, **kwargs)

You can save canvas on the media_root and upload_to path, or in temporary directory or use BytesIO object.