Встановіть 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.