Çalışma zamanında upload_to ile belirlenen Django FileField


130

Yüklemelerimi, kullanıcı joe bir dosya yüklerse, herkesin dosyalarının MEDIA_ROOT'a gitmesi yerine MEDIA_ROOT / joe'ye gidecek şekilde ayarlamaya çalışıyorum. Sorun şu ki, bunu modelde nasıl tanımlayacağımı bilmiyorum. İşte şu anda nasıl göründüğü:

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

Yani istediğim "" yerine. " upload_to olarak, kullanıcının adı olsun.

Django 1.0'dan itibaren upload_to'yu işlemek için kendi işlevinizi tanımlayabileceğinizi anlıyorum, ancak bu işlevin de kullanıcının kim olacağı konusunda hiçbir fikri olmadığı için biraz kayboldum.

Yardım için teşekkürler!

Yanıtlar:


256

Muhtemelen dokümantasyonu okudunuz , bu yüzden işte size mantıklı olması için kolay bir örnek:

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)

Gördüğünüz gibi, verilen dosya adını kullanmanıza bile gerek yok - isterseniz bunu upload_to çağrılabilir dosyanızda geçersiz kılabilirsiniz.


Evet, muhtemelen dokümanlara ait - bu IRC hakkında makul bir SSS
SmileyChris

2
Bu ModelForm ile çalışıyor mu? Örneğin, sınıf modelinin tüm niteliklerine sahip olduğunu görebiliyorum, ancak hiçbir değer yok (yalnızca alan adının bir dizesi). Şablonda kullanıcı gizlidir. Bir soru göndermem gerekebilir, bunu saatlerdir Google'da araştırıyorum.
mgag

3
İşin garibi, bu temelde aynı kurulumda bende başarısız oluyor. instance.user üzerinde hiçbir öznitelik yoktur.
Bob Spryn

11
Unix olmayan sistemlerde de çalıştığından emin olmak os.path.joinyerine kullanmak isteyebilirsiniz '/'.join. Nadir olabilirler, ancak bu iyi bir uygulamadır;)
Xudonax

2
Merhaba, aynı kodu denedim, onları models.py içine koydum ama hata alıyorum İçerik nesnesinin 'kullanıcı' özelliği yok.
Harry

12

Bu gerçekten yardımcı oldu. Biraz daha kısaca benim durumumda lambda kullanmaya karar verdim:

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

4
Bu benim için Django 1.7'de geçişleri kullanarak işe yaramadı. Bunun yerine bir işlev oluşturma sona erdi ve geçiş başladı.
aboutaaron

Str (instance.pk) kullanarak lambda'nın çalışmasını sağlayamasanız bile, istemediğinizde dosyaların üzerine yazmada sorun yaşıyorsanız iyi bir fikirdir.
Joseph Dattilo

2
örneğin pkkaydetmeden önce bir tane yok . Yalnızca güncellemeler için değil kreasyonlar (ekler) için çalışır.
Mohammad Jafar Mashhadi

lambda migrations, belgelere
Ebrahim Karimi

4

'Örnek' nesnesinin pk değerini kullanma üzerine bir not. Belgelere göre:

Çoğu durumda, bu nesne henüz veritabanına kaydedilmemiştir, bu nedenle varsayılan Otomatik Alan'ı kullanıyorsa, birincil anahtar alanı için henüz bir değere sahip olmayabilir.

Bu nedenle, pk kullanmanın geçerliliği, belirli modelinizin nasıl tanımlandığına bağlıdır.


Değer olarak Hiçbiri alıyorum. Nasıl düzelteceğimi bilemiyorum. biraz detaylı anlatır mısın?
Aman Deep

1

Taşıma işlemlerinde sorun yaşıyorsanız, muhtemelen @deconstructibledekoratör kullanmalısınız .

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

Kullanımı:

class MyModel(models.Model):
    file = models.FileField(upload_to=UploadToPath('files/%Y/%m/%d'), max_length=255)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.