Get EXIF Data from ImageField Django 2.0

Question:

I’m on this task of extracting the exif data of a photo uploaded through DJANGO 2.1.2,

Here’s my model.py

UPDATED MODEL:

class UploadedImage(models.Model):
    image = models.ImageField(
        "Uploaded image", upload_to=scramble_uploaded_filename, height_field='height', width_field='width')
    uploaded_at = models.DateTimeField(default=timezone.now)
    width = models.PositiveIntegerField(editable = False)
    height = models.PositiveIntegerField(editable = False ) 
    camera = models.CharField(max_length=10, editable = False)
    latitud = models.CharField(max_length=20, editable = False)
    longitud = models.CharField(max_length=20, editable = False)
    meta = ExifField(
        source='image',
        denormalized_fields={
            'camera': exifgetter('Model'),
            'latitud': exifgetter('GPSLatitude'),
            'longitud': exifgetter('GPSLongitude'),
        },
    )

Some guidance of how to work with this model would gladly help.

Asked By: Nicolas Abarca

||

Answers:

I also needed the EXIF information from images and could not find a good solution for that. That is why I developed a field for Django, which extracts the EXIF information using the exiftool and stores them in the database: django-exiffield.

Simple install django-exiffield, make sure the exiftool is installed and add a new field to your model:

from exiffield.fields import ExifField


class UploadedImage(models.Model):
    image = models.ImageField(
        "Uploaded image", upload_to=scramble_uploaded_filename, 
    exif = ExifField(
        source='image',
    )

Since the ExifField used JSONField internally, you can access the individual values using the appropriate key, e.g. print(image.exif['Model']).

If you need to filter your image by certain EXIF values or need to access some of them very often, it makes sense to denormalize them. The module provides an easy way to do that, eg. denormalize the camera model:

from exiffield.fields import ExifField
from exiffield.getters import exifgetter


class UploadedImage(models.Model):
    image = models.ImageField()
    camera = models.CharField(
        editable=False,
        max_length=100,
    )
    exif = ExifField(
        source='image',
        denormalized_fields={
            'camera': exifgetter('Model'),
        },
    )

You can find more information about the module in its readme.

If you have any question feel free to ask and if there are any problems with the module please open an issue in the GitHub repository!

Answered By: escaped

An alternative, instead of relying on an external package / customized field, goes below.

It saves the EXIF of the image to a JSONField which can be queried per Django’s ORM.

class UploadedImage(CreatedUpdatedByMixin, TimeStampedModel):
    image = models.ImageField(upload_to="images/")
    image_exif = models.JSONField(blank=True, null=True)

    # Class string added to store original name of image
    original_image_name = None

    # Ensure HEIC images (iPhone default) can be uploaded
    from pillow_heif import register_heif_opener
    register_heif_opener()

    # When the form is initialized save the original photo name
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.original_image_name = self.image.name

    def save(self, *args, **kwargs):

        # This checks if the photo was updated or not before saving EXIF.
        if self.original_image_name != self.image.name and not self.image.closed:

            # Extract EXIF
            image_open = Image.open(self.image)
            image_open.verify()
            image_get_exif = image_open.getexif()
            if image_get_exif:
                exif = {
                    # Ensure we're not getting "TypeError: Object of type IFDRational is not JSON serializable".
                    ExifTags.TAGS[k]: v for k, v in image_get_exif.items()
                    if k in ExifTags.TAGS and type(v) not in [bytes, TiffImagePlugin.IFDRational]
                }
                
                self.image_exif = json.dumps(exif)

       super(UploadedImage, self).save(*args, **kwargs)
Answered By: SaeX