#django #copy #filefield
#django #Копировать #поле файла
Вопрос:
У меня есть 2 простые модели:
class UploadImage(models.Model):
Image = models.ImageField(upload_to="temp/")
class RealImage(models.Model):
Image = models.ImageField(upload_to="real/")
И одна форма
class RealImageForm(ModelForm):
class Meta:
model = RealImage
Мне нужно сохранить файл из uploadImage в RealImage. Как я мог это сделать.
Приведенный ниже код не работает
realform.Image=UploadImage.objects.get(id=image_id).Image
realform.save()
Tnx за помощью.
Ответ №1:
Вдохновленный решением Джерарда, я придумал следующий код:
from django.core.files.base import ContentFile
#...
class Example(models.Model):
file = models.FileField()
def duplicate(self):
"""
Duplicating this object including copying the file
"""
new_example = Example()
new_file = ContentFile(self.file.read())
new_file.name = self.file.name
new_example.file = new_file
new_example.save()
Фактически это приведет к переименованию файла путем добавления «_1» к имени файла, чтобы и исходный файл, и эта новая копия файла могли существовать на диске одновременно.
Комментарии:
1. Я только что понял, что этот способ может быть очень тяжелым для памяти — весь файл загружается в память.
2. Копирование файла также может быть однострочным:
new_example.file = ContentFile(self.file.read(), name=self.file.name)
Ответ №2:
Хотя это поздно, но я бы решил эту проблему таким образом,
class UploadImage(models.Model):
Image = models.ImageField(upload_to="temp/")
# i need to delete the temp uploaded file from the file system when i delete this model
# from the database
def delete(self, using=None):
name = self.Image.name
# i ensure that the database record is deleted first before deleting the uploaded
# file from the filesystem.
super(UploadImage, self).delete(using)
self.Image.storage.delete(name)
class RealImage(models.Model):
Image = models.ImageField(upload_to="real/")
# in my view or where ever I want to do the copying i'll do this
import os
from django.core.files import File
uploaded_image = UploadImage.objects.get(id=image_id).Image
real_image = RealImage()
real_image.Image = File(uploaded_image, uploaded_image.name)
real_image.save()
uploaded_image.close()
uploaded_image.delete()
Если бы я использовал форму модели для обработки процесса, я просто сделаю
# django model forms provides a reference to the associated model via the instance property
form.instance.Image = File(uploaded_image, os.path.basename(uploaded_image.path))
form.save()
uploaded_image.close()
uploaded_image.delete()
обратите внимание, что я гарантирую, что файл uploaded_image закрыт, потому что вызов real_image.save() откроет файл и прочитает его содержимое. Это обрабатывается любой системой хранения, используемой экземпляром ImageField
Комментарии:
1. Обратите внимание, что с помощью image. path не будет работать с любыми «удаленными» файловыми хранилищами (такими как AWS и т.д.).
Ответ №3:
Попробуйте сделать это без использования формы. Не зная точной ошибки, которую вы получаете, я могу только предположить, что метод clean() формы выдает ошибку из-за несоответствия в параметре upload_to.
Что подводит меня к следующему пункту, если вы пытаетесь скопировать изображение из ‘temp /’ в ‘real /’, вам придется выполнить некоторую обработку файла, чтобы переместить файл самостоятельно (проще, если у вас есть PIL):
import Image
from django.conf import settings
u = UploadImage.objects.get(id=image_id)
im = Image.open(settings.MEDIA_ROOT str(u.Image))
newpath = 'real/' str(u.Image).split('/', 1)[1]
im.save(settings.MEDIA_ROOT newpath)
r = RealImage.objects.create(Image=newpath)
Надеюсь, это помогло…
Ответ №4:
У меня была такая же проблема, и я решил ее следующим образом, надеюсь, это кому-нибудь поможет:
# models.py
class A(models.Model):
# other fields...
attachment = FileField(upload_to='a')
class B(models.Model):
# other fields...
attachment = FileField(upload_to='b')
# views.py or any file you need the code in
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.core.files.base import ContentFile
from main.models import A, B
obj1 = A.objects.get(pk=1)
# You and either copy the file to an existent object
obj2 = B.objects.get(pk=2)
# or create a new instance
obj2 = B(**some_params)
tmp_file = StringIO(obj1.attachment.read())
tmp_file = ContentFile(tmp_file.getvalue())
url = obj1.attachment.url.split('.')
ext = url.pop(-1)
name = url.pop(-1).split('/')[-1] # I have my files in a remote Storage, you can omit the split if it doesn't help you
tmp_file.name = '.'.join([name, ext])
obj2.attachment = tmp_file
# Remember to save you instance
obj2.save()
Ответ №5:
Обновите решение Gerard, чтобы обрабатывать его универсальным способом:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.core.files.base import ContentFile
init_str = "src_obj." src_field_name ".read()"
file_name_str = "src_obj." src_field_name ".name"
try:
tmp_file = StringIO(eval(str(init_str)))
tmp_file = ContentFile(tmp_file.getvalue())
tmp_file.name = os.path.basename(eval(file_name_str))
except AttributeError:
tmp_file = None
if tmp_file:
try:
dest_obj.__dict__[dest_field_name] = tmp_file
dest_obj.save()
except KeyError:
pass
Используется переменная:
- src_obj = исходный объект вложения.
- src_field_name = имя FileField объекта исходного вложения.
- dest_obj = целевой объект вложения.
- dest_field_name = имя FileField объекта вложения назначения.
Комментарии:
1. Отрицательный голос за рекомендацию любого фрагмента кода, использующего eval
2. фу, особенно для загрузки файлов … ой, вей!
3. Вы можете использовать
getattr
для функциональности, которую вы использовали с eval.