Django的FileField无法保存到指定的upload_to路径

4
我有一个模型Attachment,在Django 1.4.1 应用程序中使用了FileField。这个FileField包含一个可调用的upload_to参数,根据 Django 文档 的说明,应该在表单(因此是模型)保存时调用。当我运行下面的FormTest时,upload_to不会被调用,因此文件没有出现在upload_to方法提供的位置。 我错在哪里了?

请注意,在ModelTest中通过的测试中,upload_to方法按预期工作。

我查看过的一些看起来不是问题的事项:

  • Attachment模型中属性的顺序。 我找到了一些问题,似乎表明这个顺序很重要。由于我的upload_to方法使用directory属性,所以我认为这可能会导致问题。 但它不会使方法没有被调用。
  • 也许is_valid()没有被调用。 不,我已经确认它被调用了。
  • ...

测试代码:

from core.forms.attachments import AttachmentForm
from django.test import TestCase
import unittest
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files.storage import default_storage

def suite():
    return unittest.TestSuite(
        [
            unittest.TestLoader().loadTestsFromTestCase(FormTest),
        ]
    )

class FormTest(TestCase):
    def test_form_1(self):
        filename = 'filename'
        f = file(filename)
        data = {'name':'name',}
        file_data = {'attachment_file':SimpleUploadedFile(f.name,f.read()),}
        form = AttachmentForm(data=data,files=file_data)
        self.assertTrue(form.is_valid())
        attachment = form.save()
        root_directory = 'attachments'
        upload_location = root_directory + '/' + attachment.directory + '/' + filename
        self.assertTrue(attachment.attachment_file)                 # Fails
        self.assertTrue(default_storage.exists(upload_location))    # Fails

附件模型:


from django.db import models
from parent_mixins import Parent_Mixin
import uuid
from django.db.models.signals import pre_delete,pre_save
from dirtyfields import DirtyFieldsMixin

def upload_to(instance,filename):
    return 'attachments/' + instance.directory + '/' + filename

def uuid_directory_name():
    return uuid.uuid4().hex

class Attachment(DirtyFieldsMixin,Parent_Mixin,models.Model):
    attachment_file = models.FileField(blank=True,null=True,upload_to=upload_to)
    directory = models.CharField(blank=False,default=uuid_directory_name,null=False,max_length=32)
    name = models.CharField(blank=False,default=None,null=False,max_length=128)

    class Meta:
        app_label = 'core'

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __unicode__(self):
        return unicode(self.name)

    @models.permalink
    def get_absolute_url(self):
        return('core_attachments_update',(),{'pk': self.pk})

    # def save(self,*args,**kwargs):
    #     super(Attachment,self).save(*args,**kwargs)

def pre_delete_callback(sender, instance, *args, **kwargs):
    if not isinstance(instance, Attachment): return
    if not instance.attachment_file: return
    instance.attachment_file.delete(save=False)

def pre_save_callback(sender, instance, *args, **kwargs):
    if not isinstance(instance, Attachment): return
    if not instance.attachment_file: return
    if instance.is_dirty():
        dirty_fields = instance.get_dirty_fields()
        if 'attachment_file' in dirty_fields:
            old_attachment_file = dirty_fields['attachment_file']
            old_attachment_file.delete()

pre_delete.connect(pre_delete_callback)
pre_save.connect(pre_save_callback)

附件表单:

from ..models.attachments import Attachment
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div,Layout,HTML,Field,Fieldset,Button,ButtonHolder,Submit
from django import forms

class AttachmentFormHelper(FormHelper):
    form_tag=False

    layout = Layout(
        Div(
            Div(
                Field('name',css_class='span4'),
                Field('attachment_file',css_class='span4'),
                css_class='span4',
            ),
            css_class='row',
        ),
    )

class AttachmentForm(forms.ModelForm):
    helper = AttachmentFormHelper()

    class Meta:
        fields=('attachment_file','name')
        model = Attachment

class AttachmentInlineFormHelper(FormHelper):
    form_tag=False
    form_style='inline'

    layout = Layout(
        Div(
            Div(
                Field('name',css_class='span4'),
                Field('attachment_file',css_class='span4'),
                Field('DELETE',css_class='span4'),
                css_class='span4',
            ),
            css_class='row',
        ),
    )

class AttachmentInlineForm(forms.ModelForm):
    helper = AttachmentInlineFormHelper()

    class Meta:
        fields=('attachment_file','name')
        model = Attachment

更新

我还在这些单元测试中对Attachment模型类进行了测试,所有测试都通过:

from core.models.attachments import Attachment
from core.models.attachments import upload_to
from django.test import TestCase
import unittest
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile

def suite():
    return unittest.TestSuite(
        [
            unittest.TestLoader().loadTestsFromTestCase(ModelTest),
        ]
    )

class ModelTest(TestCase):
    def test_model_minimum_fields(self):
        attachment = Attachment(name='name')
        attachment.attachment_file.save('test.txt',ContentFile("hello world"))
        attachment.save()
        self.assertEqual(str(attachment),'name')
        self.assertEqual(unicode(attachment),'name')
        self.assertTrue(attachment.directory)

    # def test_model_full_fields(self):
    #     attachment = Attachment()
    #     attachement.save()

    def test_file_operations_basic(self):
        root_directory = 'attachments'
        filename = 'test.txt'
        attachment = Attachment(name='name')
        attachment.attachment_file.save(filename,ContentFile('test'))
        attachment.save()
        upload_location = root_directory + '/' + attachment.directory + '/' + filename
        self.assertEqual(upload_to(attachment,filename),upload_location)
        self.assertTrue(default_storage.exists(upload_location))

    def test_file_operations_delete(self):
        root_directory = 'attachments'
        filename = 'test.txt'
        attachment = Attachment(name='name')
        attachment.attachment_file.save(filename,ContentFile('test'))
        attachment.save()
        upload_location = upload_to(attachment,filename)
        attachment.delete()
        self.assertFalse(default_storage.exists(upload_location))

    def test_file_operations_change(self):
        root_directory = 'attachments'
        filename_1 = 'test_1.txt'
        attachment = Attachment(name='name')
        attachment.attachment_file.save(filename_1,ContentFile('test'))
        attachment.save()
        upload_location_1 = upload_to(attachment,filename_1)
        self.assertTrue(default_storage.exists(upload_location_1))

        filename_2 = 'test_2.txt'
        attachment.attachment_file.save(filename_2,ContentFile('test'))
        attachment.save()
        upload_location_2 = upload_to(attachment,filename_2)
        self.assertTrue(default_storage.exists(upload_location_2))
        self.assertFalse(default_storage.exists(upload_location_1))

没有模板或视图。测试用例直接处理表单。 - Erik
我创建了这个测试用例,因为我使用的视图/模板无法将文件上传到上传位置。在这里提供测试用例而不是所有视图/模板代码是我让问题更简洁的方式 - 视图/模板代码中还有很多其他内容。 - Erik
1个回答

0
如果我更改“附件”模型的“attachment_file”字段,使其不允许为空或空白,则可以使测试通过。
具体更改如下:
attachment_file = models.FileField(blank=True,null=True,upload_to=upload_to)

attachment_file = models.FileField(blank=False,null=False,upload_to=upload_to)

我不知道为什么这会影响我的测试,并且很想从Django大师那里得到解释。为什么FileField在这些参数为False的情况下无法保存文件?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接