In this tutorial, we will learn how to save an image in a model from another ImageField of the same or different model.
Suppose we have a model Product
which has ImageField
named main_image
and rows in the database already have uploaded product images.
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=256)
description = models.TextField()
price = models.DecimalField(max_digits=5, decimal_places=2)
main_image = models.ImageField(upload_to='shop/%Y-%m/', null=True, blank=True)
def __str__(self):
return self.name
What if we want to upload multiple images for one product? To solve this task we will create a new model called ProductImage
.
It will have at least two fields:
- product -
ForeignKey
to Product - image -
ImageField
In models.py
class ProductImage(models.Model):
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='images',
)
image = models.ImageField(upload_to='products/%Y-%m/')
def __str__(self):
return '{} image'.format(self.product.name)
Next, we will create ProductImage
instance for each existing product and save image from its related product.
In copy_images
function we iterate over products to create ProductImage
. Inside the loop, we will save the image calling save()
method on product_image.image
. This save
method is defined in class ImageFieldFile
. We pass to this method the following arguments:
- the new name of the image which is being saved
- content of the old image by instantiating
ContentFile
class with the old image's bytes - and save=True
import os
from uuid import uuid4
from django.core.files.base import ContentFile
from . import models
def copy_images():
for product in models.Product.objects.all():
product_image = models.ProductImage.objects.create(product=product)
_, ext = os.path.splitext(product.main_image.path)
name = f'{uuid4()}{ext}'
product_image.image.save(
name,
content=ContentFile(product.main_image.read()),
save=True
)
The new image's name is defined using uuid4 value to be sure it will be unique on the filesystem, more precisely in directory products/%Y-%m
where images will be saved to. Also, here the old image's extension is kept.
Eventually, we can see the created ProductImage
instances in Django admin. Note that images on the model Product
were saved in shop/%Y-%m
directory, but copied images of the new model (ProductImage) will be saved in products/%Y-%m/
directory as the path defined in the model's declarations above in upload_to
parameter.
In Django admin, ProductImage
instances are attached to its Product
using admin.TabularInline
class. More about that class you can read in Django's official documentation:
from django.contrib import admin
from . import models
class ProductImageAdminInline(admin.TabularInline):
model = models.ProductImage
can_delete = True
extra = 1
@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin):
list_display = (
'id',
'name',
'price',
'main_image',
)
inlines = (
ProductImageAdminInline,
)