passing a callback as upload_to to FileField

谁都会走 提交于 2019-12-06 05:10:43

问题


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 for UploadItem
  • files/videos for Video

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!