2014-05-01 53 views
0

我的系統產品,與他們有聯繫,像這樣的圖片:訪問中的ModelForm上傳的文件清理()

class Product(models.Model): 
    name = models.CharField(max_length=100) 
    ... 

class Image(models.Model): 
    product = models.ForeignKey(Product) 
    image = models.ImageField(upload_to='products') 

到目前爲止好。當然,客戶希望以csv批量上傳他們的產品並上傳包含圖像的zip文件。我格式化CSV像這樣:

product_name,image_1.jpg,image_2.jpg,... 
product_2,image.jpg,... 

到目前爲止,我已經做了一個模型只是作爲輔助:

class BulkUpload(models.Model): 
    csv = models.FileField(upload_to='tmp') 
    img_zip = models.FileField(upload_to='tmp') 

的工作流程是這樣的:

  1. 用戶上傳文件通過django管理員
  2. 獲取zip文件內容並在以後存儲
  3. 解壓縮zip int o tmp目錄
  4. 開始一個事務。如果有什麼意外從這裏情況下,我們回滾
  5. 對於每一行的CSV
    1. 創建並保存產品與第一列指定的名稱。
    2. 從其他CSV字段
    3. 檢查圖像抓取圖像文件名是在壓縮,否則回滾
    4. 檢查圖像不存在目標目錄,否則回滾
    5. 移動圖像到目標目錄並將fk設置爲已保存的產品對象,並回滾任何錯誤。
  6. 提交事務
  7. 刪除ZIP和CSV,並刪除批量上傳對象(或只是不保存)

如果我們在我們以某種方式應該任意點回退通知用戶出了什麼問題。

我最初的想法是重寫保存或使用post_save信號,但沒有訪問請求意味着我既不能使用消息也不會提出驗證錯誤。在管理中重寫model_save()有它自己的問題,不能做任何驗證。

所以現在我的想法是更改ModelForm並將其提供給django管理員。我可以重寫clean()方法,引發ValidationErrors和(可能)在事務中運行我所有的東西。但我正在努力弄清楚如何以這種方式訪問​​這些文件,以便我可以在它們上使用Python的ZipFile和csv庫。在表單驗證方法中做實際的工作也感覺有點骯髒,但我不確定我能做到這一點。

我可能已經進入了太多細節,但我想解釋一下解決方案,以便可以提出其他解決方案。

回答

2

我不認爲你應該使用BulkUpload或代表此操作的任何模型,至少如果您計劃按照您當前的建議同步執行過程。我會向管理區域添加一個額外的視圖,要麼by hand要麼使用third party library,並且在那裏處理表單並執行工作流程。

但無論如何,由於您已經擁有BulkUpload型號,因此使用admin.ModelAdmin對象更容易。您的主要擔心似乎是您應該放置交易代碼的位置。正如你所提到的,有幾種選擇。在我看來,最好的選擇是將這個過程分爲兩部分:

首先,在您的模型的clean方法中,您應該檢查用戶可能產生的所有潛在錯誤:已存在的圖像,缺少圖像,重複產品等等。在這裏,你應該檢查上傳的文件是否正確,例如使用類似:

def clean(self): 
    if not zipfile.is_zipfile(self.img_zip.file): 
     raise ValidationError('Not a zip file') 

之後,你知道可以從這個點出現在會由系統誤差產生的任何錯誤:BD失敗,HD沒有足夠的空間等等,因爲在前面的步驟中應該檢查所有其他可能的錯誤。在您的ModelAdmin.save_model方法中,您應該執行工作流程的其餘部分。您可以使用ModelAdmin.message_user通知用戶任何錯誤。

至於上傳文件的實際處理方式,您可以命名它:只需在標準庫中使用zipfilecsv模塊即可。您應該創建一個ZipFile對象並將其解壓到某處。現在,您應該使用csv.reader來查看您的csv文件的數據。像這樣的東西(未測試):

def save_model(self, request, obj, form, change): 
    # ... 
    with open('tmp/' + obj.img_zip.name, 'r') as csvfile: 
      productreader = csv.reader(csvfile) 
      for product_details in productreader: 
       p = Product(name=product_details[0]) 
       p.save() 

       for image in product_details[1:]: 
        i = ImageField() 
        i.product = p 
        i.image = File(open('tmp/' + image)) # not tested 
        i.save() 

畢竟這會有在具有BulkUpload實例沒有意義的,所以應該將其刪除。這就是爲什麼我在開始時說這個模型有點沒用

顯然你需要添加交易和其他一些東西的代碼,但我希望你能得到一般的想法。

+0

我在這裏看到的問題是,除非保存()被調用,否則文件不會保存,所以我無法在模型clean()中進行驗證,據我所知。 –

+0

你說得對,但你可以使用'FileField'的文件屬性,它是InMemoryUploadedFile的一個實例。我剛剛在'clean'中更新了這個例子。 –

+0

我最後把clean()放到了ModelForm中(也許我會把它移植到模型上,稍後再清理(匆忙)),然後按照建議來做ModelAdmin的東西,它在文件系統上效率不高,但它是隻有幾個文件,它工作得很好,謝謝! –