Search code examples
djangoorm

How can I access properties of a foreign key field in django?


In my Django application, I have 2 models: Product and Image.

A product can have many images and an image can only belong to a single product.

I want to be able to create a sub-folder for the images of a product with the name of the product itself when images of a product are uploaded. So, in the Image model, I need to access the title of the product.

Here is my code:

class Product(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, blank=True, allow_unicode=True)

class Image(models.Model):
    name = models.CharField(max_length=255, null=True, blank=True)
    product = models.ForeignKey(Product, on_delete=models.CASCADE, 
                                related_name='images')
    image = models.ImageField(upload_to=f'product_images/{product.title}/')

In the Image model, I have an image field which is of type ImageField in which I want to get the title of the product.

I get the following error:

'ForeignKey' object has no attribute 'title'

How can I access the title of the product inside the Image model?


Solution

  • The issue is because this line:

    image = models.ImageField(upload_to=f'product_images/{product.title}/')
    

    Is evaluated when the model is defined, and in that context 'product.title' doesn't yet make sense: it only makes sense when an instance of the class is created with an associated product.

    To get around this, use a helper function like get_upload_to and pass it as the argument to the upload_to parameter, as below:

    def get_upload_to(instance, filename):
        return f'product_images/{instance.product.title}/{filename}'
    
    class Image(models.Model):
        name = models.CharField(max_length=255, null=True, blank=True)
        product = models.ForeignKey(
            Product, on_delete=models.CASCADE, related_name='images')
        image = models.ImageField(upload_to=get_upload_to)
    

    This method works because when the get_upload_to method is called by Django, it will automatically pass both the instance and filename variables to it (see official docs here: https://docs.djangoproject.com/en/4.2/ref/models/fields/#django.db.models.FileField.upload_to )