pythondjangodjango-modelsdjango-media

Function(instance, filename)


I,m working with my first project in Django, And I have a model like that:

def get_image_path(instance, filename):
    category_name_path = instance.category.category_name
    return f"{category_name_path}/{filename}"


class Gallery(models.Model):
    description = models.TextField()
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    image = ResizedImageField(size=[1920, 1920], upload_to=get_image_path)
    
    def __str__(self):
            return '%s %s %s' % (self.category, self.image.url, self.description)

It works but I cannot understand how the 'filename' argument is passed to the function? How it works? ;)


Solution

  • The FileField, which is a superclass of the ImageField has a method .generate_filename(…) [GitHub]:

    def generate_filename(self, instance, filename):
        if callable(self.upload_to):
            filename = self.upload_to(instance, filename)
        else:
            # …
            pass
        filename = validate_file_name(filename, allow_relative_path=True)
        return self.storage.generate_filename(filename)

    This will thus call the parameter. We can also look where the call to .generate_filename originates from:

    This is by the .save(…) method [GitHub] of the FieldFile (not to be confused with FileField, FieldFile is the file stored in the field):

    def save(self, name, content, save=True):
        name = self.field.generate_filename(self.instance, name)
        self.name = self.storage.save(name, content, max_length=self.field.max_length)
        setattr(self.instance, self.field.attname, self.name)
        self._committed = True
    
        # Save the object because it has changed, unless save is False
        if save:
            self.instance.save()

    then this simply originates from the FileField's .pre_save(…) method [GitHub] that calls .save() on the FileField:

    def pre_save(self, model_instance, add):
        file = super().pre_save(model_instance, add)
        if file and not file._committed:
            # Commit the file to storage prior to saving the model
            file.save(file.name, file.file, save=False)
        return file

    and it thus picks the .name of the FieldFile.