Django FileField với upload_to được xác định khi chạy


130

Tôi đang cố gắng thiết lập các video tải lên của mình để nếu người dùng tải lên một tệp, nó sẽ chuyển đến MEDIA_ROOT / joe thay vì để các tệp của mọi người chuyển đến MEDIA_ROOT. Vấn đề là tôi không biết cách xác định điều này trong mô hình. Đây là giao diện hiện tại:

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

Vì vậy, những gì tôi muốn là thay vì '.' là upload_to, hãy để nó là tên người dùng.

Tôi hiểu rằng kể từ Django 1.0, bạn có thể xác định chức năng của riêng mình để xử lý upload_to nhưng chức năng đó không biết người dùng sẽ là ai nên tôi hơi lạc lõng.

Cảm ơn đã giúp đỡ!

Câu trả lời:


256

Có lẽ bạn đã đọc tài liệu , vì vậy đây là một ví dụ dễ hiểu để làm cho nó có ý nghĩa:

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)

Như bạn có thể thấy, bạn thậm chí không cần sử dụng tên tệp đã cho - bạn cũng có thể ghi đè lên tên trong upload_to của mình nếu bạn muốn.


Vâng, nó có thể thuộc về tài liệu - đó là một câu hỏi thường gặp trên IRC
SmileyChris

2
Cái này có hoạt động với ModelForm không? Tôi có thể thấy thể hiện đó có tất cả các thuộc tính của mô hình lớp, nhưng không có giá trị (chỉ là một str của tên trường). Trong mẫu, người dùng bị ẩn. Tôi có thể phải gửi một câu hỏi, tôi đã googling này trong nhiều giờ.
mgag

3
Điều kỳ lạ là điều này thất bại đối với tôi về cơ bản cùng thiết lập này. instance.user không có thuộc tính trên nó.
Bob Spryn

11
Bạn có thể muốn sử dụng os.path.jointhay vì '/'.joinđể đảm bảo nó cũng hoạt động trên các hệ thống không phải Unix. Chúng có thể hiếm, nhưng đó là cách thực hành tốt;)
Xudonax

2
Xin chào, tôi đã thử cùng một mã, đặt chúng vào mô hình, nhưng gặp lỗi Đối tượng nội dung không có thuộc tính 'người dùng'.
Harry

12

Điều này thực sự có ích. Để dễ hiểu hơn một chút, quyết định sử dụng lambda trong trường hợp của tôi:

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

4
Điều này đã không làm việc cho tôi trong Django 1.7 bằng cách sử dụng di chuyển. Cuối cùng đã tạo ra một chức năng và di chuyển mất.
aboutaaron 18/03/2015

Ngay cả khi bạn không thể khiến lambda hoạt động bằng cách sử dụng str (instance.pk) là một ý tưởng tốt nếu bạn gặp vấn đề với việc ghi đè tệp khi bạn không muốn chúng.
Joseph Dattilo

2
ví dụ không có pktrước khi lưu. Nó chỉ hoạt động cho các bản cập nhật không phải sáng tạo (chèn).
Mohammad Jafar Mashhadi

lambda không migrationshoạt động trong các hoạt động vì nó không thể được tuần tự hóa theo các tài liệu
Ebrahim Karimi

4

Một lưu ý về việc sử dụng giá trị pk của đối tượng 'dụ'. Theo tài liệu:

Trong hầu hết các trường hợp, đối tượng này sẽ chưa được lưu vào cơ sở dữ liệu, vì vậy nếu nó sử dụng AutoField mặc định, nó có thể chưa có giá trị cho trường khóa chính của nó.

Do đó, tính hợp lệ của việc sử dụng pk phụ thuộc vào cách xác định mô hình cụ thể của bạn.


Tôi không nhận được giá trị nào. Tôi không thể tìm ra cách khắc phục nó. bạn có thể giải thích một chút chi tiết
Aman Deep

1

Nếu bạn có vấn đề với việc di chuyển, có lẽ bạn nên sử dụng @deconstructibletrang trí.

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))

Sử dụng:

class MyModel(models.Model):
    file = models.FileField(upload_to=UploadToPath('files/%Y/%m/%d'), max_length=255)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.