abbarnes

Django Automatically Save ImageField from URL

May 2nd, 2019
669
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.42 KB | None | 0 0
  1. #AUTOMATIC POSTER DOWNLOADING: original source: http://ishcray.com/downloading-and-saving-image-to-imagefield-in-django
  2. import imghdr  # Used to validate images
  3. #we will use pillow's copy function rather than the copy module's copy.copy()
  4. #IMPORTANT:  install Pillow instead of PIL.  import statement is the same (see Pillow docs)
  5. from PIL import Image  #Holds downloaded image and verifies it
  6. #imports changed for python3
  7. import urllib.request   # Used to download images (python 2 version: import urllib2)
  8. import urllib.parse     # Cleans up image urls (python 2 version: import urlparse)
  9. import io               # Used to imitate reading from byte file (python 2 version: import cStringIO)
  10. from django.core.files.base import ContentFile   #used to save file.  Not included in original guide
  11.  
  12. def download_image(url):
  13.     """Downloads an image and makes sure it's verified.
  14.    Returns a PIL (Pillow) Image if the image is valid, otherwise raises an exception.
  15.    """
  16.     headers = {
  17.         '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
  18.     r = urllib.request.Request(url, headers=headers)
  19.     request = urllib.request.urlopen(r, timeout=10)
  20.     # StringIO (io.BytesIO) imitates a file, needed for verification step
  21.     image_data = io.BytesIO(request.read())
  22.     # Creates an instance of PIL (Pillow) Image class - PIL (Pillow)  does the verification of file
  23.     img = Image.open(image_data)
  24.     # Verify the copied image, not original - verification requires you to open the image again after verification, but since we
  25.     # 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
  26.     # response again without remaking the request (i.e. downloading the image again). Rather than do that, we duplicate the PIL Image in memory.
  27.     img_copy = img.copy()   #uses Pillow's copy instead of the copy.copy()
  28.     if valid_img(img_copy):
  29.         return img
  30.     else:
  31.         # Maybe this is not the best error handling...you might want to just provide a path to a generic image instead
  32.         raise Exception(
  33.             'An invalid image was detected when attempting to save a Product!')
  34.  
  35. def valid_img(img):
  36.     """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() """
  37.     try:
  38.         img.verify()
  39.         return True
  40.     except:
  41.         return False
  42.     else:
  43.         return False
  44.  
  45.  
  46. class Movie(models.Model):
  47.     title = models.CharField(max_length=125)
  48.     release_year = models.IntegerField()
  49.     overview = models.CharField(max_length=325)
  50.     poster_url = models.URLField(max_length=200)
  51.     poster = models.ImageField(upload_to=poster_directory_path, null=True, blank=True)
  52.  
  53.     def save(self, *args, **kwargs):
  54.         '''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'''
  55.         url = self.poster_url
  56.         if self.poster != '' and url != '':  # Don't do anything if we don't get passed anything!
  57.             image = download_image(url)  # See function definition below
  58.             try:
  59.                 filename = urllib.parse.urlparse(url).path.split('/')[-1]
  60.                 self.poster = filename
  61.                 tempfile = image
  62.                 # Will make a file-like object in memory that you can then save
  63.                 tempfile_io = io.BytesIO()
  64.                 tempfile.save(tempfile_io, format=image.format)
  65.                 # Set save=False otherwise you will have a looping save method
  66.                 self.poster.save(filename, ContentFile(
  67.                     tempfile_io.getvalue()), save=False)
  68.             except Exception as e:
  69.                 print("Error trying to save model: saving image failed: " + str(e))
  70.                 pass
  71.         # We've gotten the image into the ImageField above...now we actually need to save it. We've redefined the save
  72.         # method for Product (Movie) , so super *should* get the parent of class Product (Movie), models.Model and then run IT'S save
  73.         # method, which will save the Product (Movie) like normal
  74.         super(Movie, self).save(*args, **kwargs)
  75.  
  76.     def __str__(self):
  77.         return self.title
Advertisement
Add Comment
Please, Sign In to add comment