Django FileField พร้อม upload_to กำหนดที่รันไทม์


130

ฉันกำลังพยายามตั้งค่าการอัปโหลดของฉันดังนั้นหากผู้ใช้ joe อัปโหลดไฟล์มันจะไปที่ MEDIA_ROOT / joe ซึ่งต่างจากการให้ไฟล์ของทุกคนไปที่ MEDIA_ROOT ปัญหาคือฉันไม่รู้ว่าจะกำหนดสิ่งนี้ในโมเดลอย่างไร นี่คือลักษณะที่ปรากฏในปัจจุบัน:

class Content(models.Model):
    name = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    file = models.FileField(upload_to='.')

ดังนั้นสิ่งที่ฉันต้องการคือแทนที่จะเป็น "." เป็น upload_to ให้เป็นชื่อผู้ใช้

ฉันเข้าใจว่าใน Django 1.0 คุณสามารถกำหนดฟังก์ชันของคุณเองเพื่อจัดการ upload_to ได้ แต่ฟังก์ชันนั้นไม่รู้ว่าผู้ใช้จะเป็นใครดังนั้นฉันจึงหลงทางเล็กน้อย

ขอบคุณสำหรับความช่วยเหลือ!

คำตอบ:


256

คุณคงอ่านเอกสารมาแล้วดังนั้นนี่คือตัวอย่างง่ายๆที่จะทำให้เข้าใจได้:

def content_file_name(instance, filename):
    return '/'.join(['content', instance.user.username, filename])

class Content(models.Model):
    name = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    file = models.FileField(upload_to=content_file_name)

อย่างที่คุณเห็นคุณไม่จำเป็นต้องใช้ชื่อไฟล์ที่กำหนดด้วยซ้ำ - คุณสามารถแทนที่ชื่อนั้นใน upload_to callable ได้เช่นกันหากต้องการ


ใช่มันอาจจะอยู่ในเอกสาร - เป็นคำถามที่พบบ่อยที่สมเหตุสมผลใน IRC
SmileyChris

2
สิ่งนี้ใช้ได้กับ ModelForm หรือไม่ ฉันเห็นว่าอินสแตนซ์มีแอตทริบิวต์ทั้งหมดของโมเดลคลาส แต่ไม่มีค่าใด ๆ (เป็นเพียง str ของชื่อฟิลด์) ผู้ใช้ซ่อนอยู่ในเทมเพลต ฉันอาจต้องส่งคำถามฉันใช้ Google นี้มาหลายชั่วโมงแล้ว
mgag

3
ผิดปกติพอที่ฉันจะล้มเหลวในการตั้งค่าเดียวกันนี้โดยทั่วไป อินสแตนซ์ผู้ใช้ไม่มีแอตทริบิวต์อยู่
Bob Spryn

11
คุณอาจต้องการใช้os.path.joinแทน'/'.joinเพื่อให้แน่ใจว่ามันใช้งานได้กับระบบที่ไม่ใช่ Unix ด้วย อาจจะหายาก แต่ก็เป็นแนวทางปฏิบัติที่ดี)
Xudonax

2
สวัสดีฉันลองใช้รหัสเดียวกันใส่ไว้ใน models.py แต่ได้รับข้อผิดพลาดวัตถุเนื้อหาไม่มีแอตทริบิวต์ "ผู้ใช้"
Harry

12

สิ่งนี้ช่วยได้จริงๆ เพื่อความกะทัดรัดอีกเล็กน้อยตัดสินใจใช้แลมด้าในกรณีของฉัน:

file = models.FileField(
    upload_to=lambda instance, filename: '/'.join(['mymodel', str(instance.pk), filename]),
)

4
สิ่งนี้ไม่ได้ผลสำหรับฉันใน Django 1.7 โดยใช้การย้ายข้อมูล จบลงด้วยการสร้างฟังก์ชันแทนและการย้ายข้อมูลใช้เวลา
aboutaaron

แม้ว่าคุณจะไม่สามารถให้แลมบ์ดาทำงานโดยใช้ str (instance.pk) ได้ก็เป็นความคิดที่ดีหากคุณมีปัญหากับการเขียนทับไฟล์เมื่อคุณไม่ต้องการ
Joseph Dattilo

2
อินสแตนซ์ไม่มีpkก่อนบันทึก ใช้ได้เฉพาะกับการอัปเดตที่ไม่ใช่การสร้างสรรค์ (ส่วนแทรก)
Mohammad Jafar Mashhadi

แลมด้าไม่ทำงานในmigrationsการดำเนินการเนื่องจากไม่สามารถต่ออนุกรมตามเอกสารได้
Ebrahim Karimi

4

หมายเหตุเกี่ยวกับการใช้ค่า pk ของวัตถุ 'อินสแตนซ์' ตามเอกสาร:

ในกรณีส่วนใหญ่ออบเจ็กต์นี้จะยังไม่ได้รับการบันทึกลงในฐานข้อมูลดังนั้นหากใช้ฟิลด์อัตโนมัติเริ่มต้นวัตถุนั้นอาจยังไม่มีค่าสำหรับฟิลด์คีย์หลัก

ดังนั้นความถูกต้องของการใช้ pk จึงขึ้นอยู่กับว่าโมเดลของคุณถูกกำหนดอย่างไร


ฉันได้รับไม่มีเป็นค่า ฉันคิดไม่ออกว่าจะแก้ไขอย่างไร คุณช่วยอธิบายรายละเอียดเล็กน้อยได้ไหม
Aman Deep

1

หากคุณมีปัญหาในการย้ายข้อมูลคุณควรใช้@deconstructibleมัณฑนากร

import datetime
import os
import unicodedata

from django.core.files.storage import default_storage
from django.utils.deconstruct import deconstructible
from django.utils.encoding import force_text, force_str


@deconstructible
class UploadToPath(object):
    def __init__(self, upload_to):
        self.upload_to = upload_to

    def __call__(self, instance, filename):
        return self.generate_filename(filename)

    def get_directory_name(self):
        return os.path.normpath(force_text(datetime.datetime.now().strftime(force_str(self.upload_to))))

    def get_filename(self, filename):
        filename = default_storage.get_valid_name(os.path.basename(filename))
        filename = force_text(filename)
        filename = unicodedata.normalize('NFKD', filename).encode('ascii', 'ignore').decode('ascii')
        return os.path.normpath(filename)

    def generate_filename(self, filename):
        return os.path.join(self.get_directory_name(), self.get_filename(filename))

การใช้งาน:

class MyModel(models.Model):
    file = models.FileField(upload_to=UploadToPath('files/%Y/%m/%d'), max_length=255)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.