#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