2012-10-23 89 views
3

我有一個Attachment模型,在Django 1.4.1應用程序中有FileField。這FileField有一個可調用的upload_to參數,其中每Django docs應該保存表格(並因此保存模型)時調用。Django FileField沒有保存到upload_to位置

當我運行下面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 

UPDATE

我也做測試的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)) 
+0

你可以發佈模板? – jpic

+0

沒有模板或視圖。測試用例直接處理表單。 – Erik

+0

我確實構建了這個測試用例,因爲我使用的視圖/模板沒有將文件上傳到上傳位置。在這裏提供測試用例而不是所有的視圖/模板代碼是我簡化問題的方式 - 視圖/模板代碼中還有很多事情要做。 – Erik

回答

0

通過測試可以做,如果我改變Attachment傳遞模型的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的文件?

相關問題