I have added the image width and height fields to an ImageField-based model:
class Photo(models.Model):
height = models.IntegerField()
width = models.IntegerField()
image = models.ImageField(
upload_to=settings.PHOTO_UPLOAD_TO,
height_field='height',
width_field='width'
)
When I do manage.py makemigrations, I am asked for the default value for existing rows. I already have many photos in the database (it's a running site), therefore I chose to add default=0 to width and height.
height = models.IntegerField(default=0)
width = models.IntegerField(default=0)
Now for all existing photos the two columns are populated with zeros, as expected. What I didn't expect however was that Photo.objects.all()[0].width and .height now return the correct non-zero values. Why? I couldn't find anything about such behaviour in documentation.
It seems the magic is being performed in Django's ImageField.update_dimension_fields method. The comments in the code say:
Update field's width and height fields, if defined.
This method is hooked up to model's post_init signal to update dimensions after instantiating a model instance. However, dimensions won't be updated if the dimensions fields are already populated. This avoids unnecessary recalculation when loading an object from the database.
Dimensions can be forced to update with
force=True, which is howImageFileDescriptor.__set__calls this method.
So when instantiating the model, update_dimension_fields reads the dimensions from the image file and stores those on the instance. This will incur a little hit in a live environment.
Saving all images will store the dimensions in the database:
In [1]: for p in Photo.objects.all():
...: p.save()
The documentation says https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ImageField.height_field
ImageField.height_field:
Name of a model field which will be auto-populated with the height of the image each time the model instance is saved.
model save() must have auto-populated both the height and width