実行時にupload_toが決定されたDjango FileField


130

すべてのユーザーのファイルをMEDIA_ROOTに移動させるのではなく、ユーザーjoeがファイルをアップロードするとMEDIA_ROOT / joeに移動するように、アップロードを設定しようとしています。問題は、モデルでこれを定義する方法がわからないことです。現在の外観は次のとおりです。

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呼び出し可能ファイルでオーバーライドすることもできます。


ええ、それはおそらくドキュメントに属します-それはIRCに関する合理的なFAQです
SmileyChris 09

2
これはModelFormで動作しますか?インスタンスにはクラスモデルのすべての属性があることがわかりますが、値はありません(フィールド名のstrのみ)。テンプレートでは、ユーザーは非表示になっています。私は質問を提出しなければならないかもしれません、私はこれを何時間もグーグルで使ってきました。
mgag

3
奇妙なことに、これは基本的にこれと同じ設定で失敗しています。instance.userには属性がありません。
Bob Spryn 2012

11
os.path.join代わりにを使用して、'/'.joinUnix以外のシステムでも機能することを確認してください。それらはまれかもしれませんが、それは良い習慣です;)
Xudonax

2
こんにちは、私は同じコードを試して、models.pyに入れましたが、エラーが発生しましたContentオブジェクトには属性 'user'がありません。
ハリー

12

これは本当に役に立ちました。もう少し簡潔にするために、私の場合はラムダを使用することにしました:

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

4
これは、Django 1.7でマイグレーションを使用する場合は機能しませんでした。代わりに関数を作成することになり、移行がかかりました。
aboutaaron 2015年

str(instance.pk)を使用してラムダを機能させることができない場合でも、ファイルの上書きで問題が発生したくない場合は、これをお勧めします。
ジョセフダッティロ2016年

2
pk保存する前にインスタンスがありません。作成(挿入)ではなく更新でのみ機能します。
Mohammad Jafar Mashhadi 2017

ラムダはドキュメントmigrationsに従ってシリアル化できないため、操作では機能しません
Ebrahim Karimi

4

「インスタンス」オブジェクトのpk値の使用に関する注意。ドキュメントによると:

ほとんどの場合、このオブジェクトはまだデータベースに保存されていないため、デフォルトのAutoFieldを使用する場合、主キーフィールドの値がまだない可能性があります。

したがって、pkの使用の有効性は、特定のモデルの定義方法によって異なります。


値としてNoneを取得しています。修正方法がわかりません。少し詳しく説明してください。
アマンディープ

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.