Django'nun Dosya Alanını mevcut bir dosyaya ayarlayın


90

Diskte mevcut bir dosyam (/folder/file.txt diyelim) ve Django'da bir FileField model alanım var.

Ben yaparken

instance.field = File(file('/folder/file.txt'))
instance.save()

dosyayı olarak yeniden kaydeder file_1.txt(bir dahaki sefere _2, vb.).

Nedenini anlıyorum, ancak bu davranışı istemiyorum - alanın ilişkilendirilmesini istediğim dosyanın gerçekten orada beni beklediğini biliyorum ve sadece Django'nun onu göstermesini istiyorum.

Nasıl?


1
Django'yu veya alt sınıfları değiştirmeden istediğinizi elde edebileceğinizden emin değilim FileField. Bir FileFieldkaydedildiğinde, dosyanın yeni bir kopyası oluşturulur. Bundan kaçınmak için bir seçenek eklemek oldukça basit olacaktır.
Michael Mior

evet, alt sınıflara girmem ve bir param eklemem gerekiyor gibi görünüyor. Bu basit görev için fazladan tablo oluşturmak istemiyorum
Koruma

1
Dosyayı farklı bir konuma koyun, bu yolla alanınızı oluşturun, kaydedin ve ardından dosyayı, yükleme hedefine alın.
benjaoming

Yanıtlar:


22

Bunu kalıcı olarak yapmak istiyorsanız, kendi FileStorage sınıfınızı oluşturmanız gerekir.

import os
from django.conf import settings
from django.core.files.storage import FileSystemStorage

class MyFileStorage(FileSystemStorage):

    # This method is actually defined in Storage
    def get_available_name(self, name):
        if self.exists(name):
            os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return name # simply returns the name passed

Şimdi modelinizde, değiştirilmiş MyFileStorage'ınızı kullanıyorsunuz

from mystuff.customs import MyFileStorage

mfs = MyFileStorage()

class SomeModel(model.Model):
   my_file = model.FileField(storage=mfs)

oh, umut verici görünüyor. cuase the FileField'ın kodu sezgisel değil
Guard

ancak ... depolama alanını istek bazında değiştirmek mümkün mü, örneğin: instance.field.storage = mfs; instance.field.save (ad, dosya); ancak bunu kodumun farklı bir dalında yapmıyorum
Koruma

2
Hayır, depolama motoru modele bağlı olduğu için. Tüm bunlardan kaçınmak için dosya yolunuzu ya a FilePathFieldya da basitçe düz metin olarak kaydedebilirsiniz .
Burhan Khalid

Bir adı geri döndüremezsin. Önce mevcut dosyayı kaldırmanız gerekir.
Alexander Shpindler

124

sadece instance.field.namedosyanızın yoluna ayarlayın

Örneğin

class Document(models.Model):
    file = FileField(upload_to=get_document_path)
    description = CharField(max_length=100)


doc = Document()
doc.file.name = 'path/to/file'  # must be relative to MEDIA_ROOT
doc.file
<FieldFile: path/to/file>

15
MEDIA_ROOTYani sizden göreceli yol .
mgalgs

7
Bu örnekte, şunu da yapabileceğinizi düşünüyorumdoc.file = 'path/to/file'
Andrew Swihart

14

bunu dene ( doc ):

instance.field.name = <PATH RELATIVE TO MEDIA_ROOT> 
instance.save()

5

Kendi depolama sınıfını yazmak doğru. Ancak get_available_name, geçersiz kılmak için doğru yöntem değildir.

get_available_nameDjango aynı ada sahip bir dosya gördüğünde ve yeni bir kullanılabilir dosya adı almaya çalıştığında çağrılır. Yeniden adlandırmaya neden olan yöntem değildir. yöntem buna neden oldu _save. İçindeki yorumlar _saveoldukça iyidir ve os.O_EXCLaynı dosya adı zaten mevcutsa bir OSError atacak olan bayrakla yazmak için dosyayı açmayı kolayca bulabilirsiniz . Django bu Hatayı yakalar ve ardından çağırırget_available_name yeni bir ad almak için .

Bu yüzden doğru yolun geçersiz kılmak _saveve os.open () 'i bayraksız çağırmak olduğunu düşünüyorum os.O_EXCL. Değişiklik oldukça basit ancak yöntem biraz uzun, bu yüzden buraya yapıştırmıyorum. Daha fazla yardıma ihtiyacın olursa söyle :)


kopyalamanız gereken 50 satırlık koddur ki bu oldukça kötüdür. Gelecekte Django'nun yeni sürümlerine yükseltme yapmak için get_available_name'i geçersiz kılmak daha izole, daha kısa ve çok daha güvenli görünüyor
Michael Gendin

2
Yalnızca geçersiz kılma sorunu, get_available_nameaynı ada sahip bir dosya yüklediğinizde, sunucunun sonsuz bir döngüye girmesidir. Yana _saveve kontroller dosya adı ancak yeni bir tane almak için karar get_available_namehala yinelenen birini verir. Yani ikisini de geçersiz kılmanız gerekiyor.
x1a0

1
Oops, bu tartışmayı iki soru halinde yapıyoruz, ancak sadece şimdi onların biraz farklı olduklarını fark ettim) Yani bu sorunun içindeyim ve siz de bu
sorunun içindesiniz

1

Ben tam olarak aynı problem vardı! sonra buna Modellerimin neden olduğunu fark ettim. Örnek modellerimden şöyle bir şey aldım:

class Tile(models.Model):
  image = models.ImageField()

Ardından, diskte aynı dosyayı referans alan daha fazla kutucuğa sahip olmak istedim! Bunu çözmek için bulduğum yol, Model yapımı şu şekilde değiştirmekti:

class Tile(models.Model):
  image = models.ForeignKey(TileImage)

class TileImage(models.Model):
  image = models.ImageField()

Bunun daha mantıklı olduğunu anladıktan sonra, çünkü aynı dosyanın DB'mde birden fazla kaydedilmesini istiyorsam, bunun için başka bir tablo oluşturmalıyım!

Sanırım probleminizi de bu şekilde çözebilirsiniz, sadece modelleri değiştirebileceğinizi umuyoruz!

DÜZENLE

Ayrıca, örneğin şunun gibi farklı bir depolama alanı kullanabileceğinizi tahmin ediyorum:

http://code.welldev.org/django-storages/src/11bef0c2a410/storages/backends/symlinkorcopy.py


senin durumunda mantıklı, benimkinde değil. Birden çok kez başvurulmasını istemiyorum. Bir dosyaya referans veren bir nesne oluşturuyorum, sonra diğer çekimlerdeki hatalar olduğunu fark ediyorum ve oluşturma formunu yeniden açıyorum. Yeniden gönderildiğinde, diske zaten kaydedilmiş olan dosyayı kaybetmek istemiyorum
Guard

bu yüzden benim yaklaşımımı kullanabilirsin! çünkü dosyayı ancak o zaman alacağınız bir FormFile tablosuna sahip olacaksınız! daha sonra Form tablonuzda bu dosya için bir FK'ye sahip olacaksınız! böylece aynı dosya için yeni formları değiştirebilir / oluşturabilirsiniz! (btw ana örneğimde FK'nin sırasını değiştiriyorum)
Arthur Neves

Alanınızı (modellerinizi) gönderinize eklemek istiyorsanız! Ben de daha iyi bir fikrim olabilir!
Arthur Neves

etki alanı aslında önemli değil - kendisiyle ilişkilendirilmiş bir fotoğrafın bulunduğu bir modelim var ve özel düzenleme ekranım var. yüklendikten sonra fotoğrafın sunucuda kalmasını istiyorum, ancak aslında ayrı bir model, tablo ve FK araması yapmayı sevmiyorum çünkü bunlar bir çerçeve sınırlaması gibi görünüyor
Guard

Sanırım buradaki sınırlama, django'da bir FileField kaydettiğinizde, her zaman Django Depolarından geçmesidir! bu yüzden bir dosya yolunu zorlamanız mantıklı olmayacak! Ayrıca Django, dosyanın yolda zaten var olduğunu nasıl bilmeli? Kullanabileceğiniz başka bir yaklaşım, bunun yerine FilePathField kullanmaktır! böylece DB'nizdeki yolu belirleyebilir ve aramayı en iyi olduğunu düşündüğünüz şekilde yapabilirsiniz!
Arthur Neves

1

Kendi depolama alanınızı tanımlamalı, onu FileSystemStorage'dan devralmalı ve OS_OPEN_FLAGSsınıf özniteliğini ve get_available_name()yöntemi geçersiz kılmalısınız :

Django Sürümü: 3.1

Proje / çekirdek / dosyalar / depolar / arka uçlar / local.py

import os

from django.core.files.storage import FileSystemStorage


class OverwriteStorage(FileSystemStorage):
    """
    FileSystemStorage subclass that allows overwrite the already existing
    files.
    
    Be careful using this class, as user-uploaded files will overwrite
    already existing files.
    """

    # The combination that don't makes os.open() raise OSError if the
    # file already exists before it's opened.
    OS_OPEN_FLAGS = os.O_WRONLY | os.O_TRUNC | os.O_CREAT | getattr(os, 'O_BINARY', 0)

    def get_available_name(self, name, max_length=None):
        """
        This method will be called before starting the save process.
        """
        return name

Modelinizde, özel OverwriteStorage'ınızı kullanın

myapp / models.py

from django.db import models

from core.files.storages.backends.local import OverwriteStorage


class MyModel(models.Model):
   my_file = models.FileField(storage=OverwriteStorage())
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.