Встановіть FileField Django як існуючий файл


89

У мене є наявний файл на диску (скажімо /folder/file.txt) і поле моделі FileField у Django.

Коли я це роблю

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

він повторно зберігає файл як file_1.txt(наступного разу _2, тощо).

Я розумію, чому, але я не хочу такої поведінки - я знаю, що файл, з яким хочу пов’язати поле, справді чекає мене, і я просто хочу, щоб Django вказав на нього.

Як?


1
Не впевнені, що можете отримати бажане, не змінюючи Django або не підкласуючи FileField. Щоразу, коли a FileFieldзберігається, створюється нова копія файлу. Було б досить просто додати можливість уникнути цього.
Michael Mior

ну так, схоже, я повинен підклас і додати параметр. Я не хочу створювати додаткові таблиці для цього простого завдання
Охорона

1
Помістіть файл в інше місце, створіть своє поле за цим шляхом, збережіть його, і тоді у вас буде файл у пункті upload_to.
benjaoming

Відповіді:


22

Якщо ви хочете зробити це постійно, вам потрібно створити власний клас FileStorage

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

Тепер у вашій моделі ви використовуєте модифікований MyFileStorage

from mystuff.customs import MyFileStorage

mfs = MyFileStorage()

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

о, виглядає багатообіцяюче. Тим не менше, код FileField є зовсім не інтуїтивним
Guard

але ... чи можна змінити сховище на основі кожного запиту, наприклад: instance.field.storage = mfs; instance.field.save (ім'я, файл); але не робити цього в іншій гілці мого коду
Guard

2
Ні, оскільки механізм зберігання прив'язаний до моделі. Ви можете уникнути всього цього, просто зберігаючи шлях до файлу або як, FilePathFieldабо просто як звичайний текст.
Бурхан Халід

Ви не можете просто повернути ім’я. Спочатку потрібно видалити наявний файл.
Олександр Шпіндлер,

124

просто встановити instance.field.name шлях до вашого файлу

напр

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_ROOT, тобто.
mgalgs

7
У цьому прикладі, я думаю, ви також можете просто зробитиdoc.file = 'path/to/file'
Ендрю Свіхарт,


5

Правильно писати власний клас пам’яті. Однак get_available_nameце не правильний метод, який можна замінити.

get_available_nameвикликається, коли Django бачить файл із таким іменем і намагається отримати нове доступне ім'я файлу. Не метод викликає перейменування. викликаний метод, тобто _save. Коментарі в _saveдосить хороші, і ви можете легко знайти його, що відкриває файл для запису з прапором, os.O_EXCLякий видасть помилку OSError, якщо те саме ім'я файлу вже існує. Django виявляє цю помилку, а потім дзвонить, get_available_nameщоб отримати нову назву.

Тому я думаю, що правильним способом є перевизначення _saveта виклик os.open () без прапора os.O_EXCL. Модифікація досить проста, проте метод трохи довгий, тому я не вставляю його сюди. Скажіть, якщо вам потрібна додаткова допомога :)


це 50 рядків коду, який ви повинні скопіювати, що досить погано. Перевизначення get_available_name здається більш ізольованим, коротшим та набагато безпечнішим для, скажімо, оновлення до нових версій Django в майбутньому
Майкл Гендін,

2
Проблема лише перевизначення get_available_nameполягає в тому, що коли ви завантажуєте файл з тим самим іменем, сервер потрапляє в нескінченний цикл. Оскільки _saveперевіряє ім'я файлу і вирішує отримати новий, проте get_available_nameвсе одно повертає дублікат. Тож вам потрібно замінити обидва.
x1a0

1
На жаль, ми обговорюємо це питання у двох питаннях, але тільки зараз я помітив, що вони дещо відрізняються) Тож я правий у цьому питанні, а ви - у цьому)
Майкл Гендін,

1

У мене була точно така ж проблема! тоді я усвідомлюю, що мої Моделі спричинили це. Наприклад я ненавиджу свої моделі так:

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

Потім я хотів мати більше однієї плитки, що посилається на той самий файл на диску! Я вирішив це шляхом зміни своєї структури Моделі на таку:

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

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

Що після того, як я усвідомлюю, що це має більше сенсу, тому що якщо я хочу, щоб той самий файл зберігався більше, ніж один у моїй БД, я повинен створити для нього іншу таблицю!

Я думаю, ви можете вирішити свою проблему так само, сподіваючись, що ви можете змінити моделі!

РЕДАГУВАТИ

Крім того, я думаю, ви можете використовувати інший сховище, наприклад, наприклад: SymlinkOrCopyStorage

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


має сенс у вашому випадку, а не в моєму. Я не хочу, щоб на нього посилалися кілька разів. Я створюю об'єкт, що посилається на файл, потім розумію, що в інших атрибутах є помилки, і знову відкриваю форму створення. При його повторному поданні я не хочу втратити файл, який вже збережений на диску
Guard

тож я здогадуюсь ти можеш використати мій підхід! тому що у вас буде таблиця FormFile, в якій буде міститися файл лише тоді! тоді у вашій таблиці форм ви будете мати FK для цього файлу! тож Ви можете змінити / створити нові форми для того самого файлу! (до речі, я міняю порядок ФК у своєму головному прикладі)
Артур Невес,

Якщо ви хочете розмістити свій домен (моделі) у своєму дописі! я теж маю кращу ідею!
Артур Невес,

домен насправді не має значення - у мене є модель із фотографією, пов’язаною з нею, і у мене є спеціальний екран редагування. після завантаження я хочу, щоб фотографія залишалася на сервері, але мені насправді не подобається створювати окрему модель, таблицю та пошук FK лише тому, що це, схоже, обмеження фреймворку
Guard

Я маю на увазі обмеження, оскільки, коли ви зберігаєте FileField у django, він завжди проходить через Django Storages! так що не буде сенсу просто примушувати шлях до файлу! також як Django повинен знати, що файл вже існує у шляху? інший підхід, який ви можете використовувати, - використання замість цього FilePathField! так що ви можете просто встановити шлях у своїй БД і зробити пошук таким, як вважаєте найкращим!
Артур Невес,

1

Ви повинні визначити своє власне сховище, успадкувати його від FileSystemStorage і замінити OS_OPEN_FLAGSатрибут і get_available_name()метод класу :

Версія Django: 3.1

Project / core / files / storages / backends / 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

У вашій моделі використовуйте власний OverwriteStorage

myapp / models.py

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


class MyModel(model.Model):
   my_file = model.FileField(storage=OverwriteStorage)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.