Django 具有当前实例id的动态文件上载路径

Django 具有当前实例id的动态文件上载路径,django,file-upload,django-views,Django,File Upload,Django Views,我有一个表格,其中包含当前登录用户、一些输入和一个文件: class AddItemForm(ModelForm): class Meta: model = Item exclude = ['user'] 对于此表单,请查看: item_form = AddItemForm(request.POST, request.FILES) if item_form.is_valid(): item = item_form.save(commit=Fals

我有一个表格,其中包含当前登录用户、一些输入和一个文件:

class AddItemForm(ModelForm):
    class Meta:
        model = Item
        exclude = ['user']
对于此表单,请查看:

item_form = AddItemForm(request.POST, request.FILES)
if item_form.is_valid():
    item = item_form.save(commit=False)
    item.user = request.user
    item.save()
对于此项目的文件字段,我使用上载功能。以下是我的观点:

class Item(models.Model):
    user = models.ForeignKey(User)
    cover_image = models.FileField(upload_to=get_upload_path)

def get_upload_path(instance, filename):

    return "items/user_{user_id}/item_{item_id}/{filename}".format(user_id=instance.user.id, item_id=instance.id,filename=filename)
问题是,我无法在上载路径中看到当前实例id,因为以下行:

item = item_form.save(commit=False)
它还没有实例id,而是创建了user\u 1/item\u NONE/file来代替当前的item id

如何设置此路径的id

提前感谢

我找到了基于使用post_保存信号的想法和代码,当创建的对象从临时目录移动到模型类中的指定目录时:

使用密钥和上传密钥是可选的。use_key默认为False。如果为True,则实例的id将用作新文件的前缀,因为现在我们正在移动该文件,因此有可能被覆盖。upload_to将简单地定义文件最初上载到的临时目录


更新至django新版本的新使用方式信号:

from django.db.models import ImageField, FileField, signals
from django.conf import settings
import shutil, os, glob, re
from distutils.dir_util import mkpath

class CustomImageField(ImageField):
    """Allows model instance to specify upload_to dynamically.

    Model class should have a method like:

        def get_upload_to(self, attname):
            return 'path/to/{0}'.format(self.id)
    """
    def __init__(self, *args, **kwargs):
        kwargs['upload_to'] = kwargs.get('upload_to', 'tmp')

        try:
            self.use_key = kwargs.pop('use_key')
        except KeyError:
            self.use_key = False

        super(CustomImageField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        """Hook up events so we can access the instance."""
        super(CustomImageField, self).contribute_to_class(cls, name)
        signals.post_save.connect(self._move_image, sender=cls)

    def _move_image(self, instance, **kwargs):
        """
            Function to move the temporarily uploaded image to a more suitable directory 
            using the model's get_upload_to() method.
        """
        if hasattr(instance, 'get_upload_to'):
            src = getattr(instance, self.attname)
            if src:
                m = re.match(r"%s/(.*)" % self.upload_to, str(src))
                if m:
                    if self.use_key:
                        dst = "%s/%d_%s" % (instance.get_upload_to(self.attname), instance.id, m.groups()[0])
                    else:
                        dst = "%s/%s" % (instance.get_upload_to(self.attname), m.groups()[0])
                    basedir = "%s/%s/" % (settings.MEDIA_ROOT, os.path.dirname(dst))
                    mkpath(basedir)
                    shutil.move("%s/%s" % (settings.MEDIA_ROOT, src),"%s/%s" % (settings.MEDIA_ROOT, dst))
                    setattr(instance, self.attname, dst)
                    instance.save()

    def db_type(self):
        """Required by Django for ORM."""
        return 'varchar(100)'

您不能在
get\u upload\u path
中为新记录使用
instance.id
,因为此时尚未分配属性。它仅在更新现有记录时起作用。
from django.db.models import ImageField, FileField, signals
from django.conf import settings
import shutil, os, glob, re
from distutils.dir_util import mkpath

class CustomImageField(ImageField):
    """Allows model instance to specify upload_to dynamically.

    Model class should have a method like:

        def get_upload_to(self, attname):
            return 'path/to/{0}'.format(self.id)
    """
    def __init__(self, *args, **kwargs):
        kwargs['upload_to'] = kwargs.get('upload_to', 'tmp')

        try:
            self.use_key = kwargs.pop('use_key')
        except KeyError:
            self.use_key = False

        super(CustomImageField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        """Hook up events so we can access the instance."""
        super(CustomImageField, self).contribute_to_class(cls, name)
        signals.post_save.connect(self._move_image, sender=cls)

    def _move_image(self, instance, **kwargs):
        """
            Function to move the temporarily uploaded image to a more suitable directory 
            using the model's get_upload_to() method.
        """
        if hasattr(instance, 'get_upload_to'):
            src = getattr(instance, self.attname)
            if src:
                m = re.match(r"%s/(.*)" % self.upload_to, str(src))
                if m:
                    if self.use_key:
                        dst = "%s/%d_%s" % (instance.get_upload_to(self.attname), instance.id, m.groups()[0])
                    else:
                        dst = "%s/%s" % (instance.get_upload_to(self.attname), m.groups()[0])
                    basedir = "%s/%s/" % (settings.MEDIA_ROOT, os.path.dirname(dst))
                    mkpath(basedir)
                    shutil.move("%s/%s" % (settings.MEDIA_ROOT, src),"%s/%s" % (settings.MEDIA_ROOT, dst))
                    setattr(instance, self.attname, dst)
                    instance.save()

    def db_type(self):
        """Required by Django for ORM."""
        return 'varchar(100)'