6

I have an abstract model class UploadItem for handling uploaded files. I want each subclass to be able to define the upload_to path. For this, i pass a callback to the constructor of FileField.

This is an example:

class UploadItem(models.Model):
    file = models.FileField(upload_to=UploadItem.get_directory) 


    class Meta:
        abstract = True
# I want videos to be storred in 'videos/' directory
class Video(UploadItem):
    def get_directory(self, instance, filename):
        return 'videos/'

But this doesn't work, i am getting this error:

file = models.FileField(upload_to=UploadItem.get_directory) 
NameError: name 'UploadItem' is not defined

2 Answers 2

7

The error is natural given that at the time of evaluating

file = models.FileField(upload_to=UploadItem.get_directory) 

the UploadItem class is not yet defined. You can do the following to make it work:

def get_directory():
    pass

class UploadItem(models.Model):
    file = models.FileField(upload_to=get_directory)

    class Meta:
        abstract = True

This won't solve all your problems though. Adding (or overriding) a method get_directory in the Video class will not change the upload_to property of the file attribute of the model.

Update

The documentation says that the upload_to can be a callable.

This may also be a callable, such as a function, which will be called to obtain the upload path, including the filename. This callable must be able to accept two arguments, and return a Unix-style path (with forward slashes) to be passed along to the storage system.

Given this we can write a custom call back function like this:

categories_and_paths = { 'video': 'videos/', 'photo': 'photos/' } # etc.
def get_directory(instance, filename):
    category = instance.category
    return categories_and_paths.get(category, '')

Instance here will be the instance of the respective model. For this to work each model instance should have a category field. We can add one in the body of the model.

class Video(UploadItem):
    category = 'video'
Sign up to request clarification or add additional context in comments.

2 Comments

so what do you suggest to achieve whati i want to do? I want each subclass to redefine where to upload files. thanks
I see, i wanted the callback to be part of the class. But this is not possible. I will do it this way.
7

This can be done in a similar fashion with some tweaks if you need to use base class properties as part of a subclass:

import os

# ...

def get_directory(instance, filename):
    return instance.get_file_directory(filename)


class UploadItem(models.Model):
    FILE_DIRECTORY = 'files/'

    file = models.FileField(upload_to=get_directory) 

    class Meta:
        abstract = True

    @staticmethod
    def get_file_directory(filename):
        return os.path.join(UploadItem.FILE_DIRECTORY, filename)

And then in subclass:

class Video(UploadItem):
    FILE_DIRECTORY = 'videos/'

    def get_file_directory(self, filename):
        filename = os.path.join(self.FILE_DIRECTORY, filename)
        return super().get_document_path(filename)

This way you will have upload_to that equals:

  • files for UploadItem
  • files/videos for Video

Might be useful for more complex objects that require to share some common base property.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.