#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