问题
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
回答1:
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'
回答2:
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
forUploadItem
files/videos
forVideo
Might be useful for more complex objects that require to share some common base property.
来源:https://stackoverflow.com/questions/3669830/passing-a-callback-as-upload-to-to-filefield