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

时间:2014-10-31 19:03:53

标签: 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=False)
    item.user = request.user
    item.save()

这个项目的文件字段我正在使用upload_to功能。这是我的模态:

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_1 / item_NONE / file

而不是当前项ID

如何将id设置为此路径?

提前致谢

2 个答案:

答案 0 :(得分:1)

Here我找到了想法&&基于使用post_save信号的代码,当创建的对象从temp目录移动到模型类中的指定目录时:

  

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

from django.db.models import ImageField, FileField, signals
from django.dispatch import dispatcher
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)
        dispatcher.connect(self._move_image, signal=signals.post_save, sender=cls)

    def _move_image(self, instance=None):
        """
            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, 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)'


class Image(models.Model):
    file = CustomImageField(use_key=True, upload_to='tmp')

    def get_upload_to(self, attname):
        return 'path/to/{0}'.format(self.id)

答案 1 :(得分:0)

更新了新版本的 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)'