abbarnes icon

Django Automatically Save ImageField from URL

abbarnes | PRO | 05/02/19 07:21:16 PM UTC | 0 ⭐ | 635 👁️ | Never ⏰ | []
Python |

4.42 KB

|

None

|

0 👍

/

0 👎

#AUTOMATIC POSTER DOWNLOADING: original source: http://ishcray.com/downloading-and-saving-image-to-imagefield-in-django
import imghdr  # Used to validate images
#we will use pillow's copy function rather than the copy module's copy.copy()
#IMPORTANT:  install Pillow instead of PIL.  import statement is the same (see Pillow docs)
from PIL import Image  #Holds downloaded image and verifies it 
#imports changed for python3
import urllib.request   # Used to download images (python 2 version: import urllib2)
import urllib.parse     # Cleans up image urls (python 2 version: import urlparse)
import io               # Used to imitate reading from byte file (python 2 version: import cStringIO)
from django.core.files.base import ContentFile   #used to save file.  Not included in original guide
 
def download_image(url):
    """Downloads an image and makes sure it's verified.
    Returns a PIL (Pillow) Image if the image is valid, otherwise raises an exception.
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0'}  # More likely to get a response if server thinks you're a browser
    r = urllib.request.Request(url, headers=headers)
    request = urllib.request.urlopen(r, timeout=10)
    # StringIO (io.BytesIO) imitates a file, needed for verification step
    image_data = io.BytesIO(request.read())
    # Creates an instance of PIL (Pillow) Image class - PIL (Pillow)  does the verification of file
    img = Image.open(image_data)
    # Verify the copied image, not original - verification requires you to open the image again after verification, but since we 
    # don't have the file saved yet we won't be able to. This is because once we read() urllib2.urlopen we can't access the 
    # response again without remaking the request (i.e. downloading the image again). Rather than do that, we duplicate the PIL Image in memory.
    img_copy = img.copy()   #uses Pillow's copy instead of the copy.copy()
    if valid_img(img_copy):
        return img
    else:
        # Maybe this is not the best error handling...you might want to just provide a path to a generic image instead
        raise Exception(
            'An invalid image was detected when attempting to save a Product!')
 
def valid_img(img):
    """Verifies that an instance of a PIL Image Class is actually an image and returns either True or False.  Note: Copying img returns an instance of the Pillow/PIL Image class, regardless of whether you use copy.copy or Image.copy.  So all i'm doing here is checking with pillow's Image.verify() """
    try:
        img.verify()
        return True
    except:
        return False
    else:
        return False
 
 
class Movie(models.Model):
    title = models.CharField(max_length=125)
    release_year = models.IntegerField()
    overview = models.CharField(max_length=325)
    poster_url = models.URLField(max_length=200)
    poster = models.ImageField(upload_to=poster_directory_path, null=True, blank=True)
 
    def save(self, *args, **kwargs):
        '''I take the url from a field in my Movie model, but feel free to add it back as an argument for the save function'''
        url = self.poster_url
        if self.poster != '' and url != '':  # Don't do anything if we don't get passed anything!
            image = download_image(url)  # See function definition below
            try:
                filename = urllib.parse.urlparse(url).path.split('/')[-1]
                self.poster = filename
                tempfile = image
                # Will make a file-like object in memory that you can then save
                tempfile_io = io.BytesIO()
                tempfile.save(tempfile_io, format=image.format)
                # Set save=False otherwise you will have a looping save method
                self.poster.save(filename, ContentFile(
                    tempfile_io.getvalue()), save=False)
            except Exception as e:
                print("Error trying to save model: saving image failed: " + str(e))
                pass
        # We've gotten the image into the ImageField above...now we actually need to save it. We've redefined the save 
        # method for Product (Movie) , so super *should* get the parent of class Product (Movie), models.Model and then run IT'S save 
        # method, which will save the Product (Movie) like normal
        super(Movie, self).save(*args, **kwargs)
 
    def __str__(self):
        return self.title

Comments