プログラムで画像をDjango ImageFieldに保存する


203

わかりました、ほぼすべてを試しましたが、これを機能させることはできません。

  • ImageFieldが設定されたDjangoモデルがあります
  • HTTP経由で画像をダウンロードするコードがあります(テスト済みで動作)
  • 画像は直接 'upload_to'フォルダーに保存されます(upload_toはImageFieldで設定されるものです)
  • 私がする必要があるのは、既存の画像ファイルのパスをImageFieldに関連付けることだけです。

このコードは6つの方法で記述しました。

私が実行している問題は、私が書いているすべてのコードで、次の動作になります。(1)Djangoは2番目のファイルを作成します。(2)新しいファイルの名前を変更し、ファイルの末尾に_を追加します次に、(3)基本的に空の名前が変更されたファイルを残して、データを転送しません。「upload_to」パスに残っているのは2つのファイルです。1つは実際のイメージで、もう1つはイメージの名前ですが、空です。もちろん、ImageFieldパスは、Djangoが作成しようとした空のファイルに設定されます。

それが不明確な場合のために、私は説明しようとします:

## Image generation code runs.... 
/Upload
     generated_image.jpg     4kb

## Attempt to set the ImageField path...
/Upload
     generated_image.jpg     4kb
     generated_image_.jpg    0kb

ImageField.Path = /Upload/generated_image_.jpg

Djangoにファイルを再保存させずにこれを行うにはどうすればよいですか?私が本当に欲しいのは、この効果に対するものです...

model.ImageField.path = generated_image_path

...しかし、それはうまくいきません。

そして、はい、私はのような、ここで他の質問を経てきたこの1と同様に上のDjangoのドキュメントファイル

UPDATE さらにテストを行った後、この動作は、Windowsサーバー上のApacheで実行されている場合にのみ行われます。XPの「runserver」で実行している間は、この動作は実行されません。

私は困惑しています。

XPで正常に実行されるコードは次のとおりです...

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()

Djangoのもう1つの素晴らしい質問です。私はこの問題を解決するために数回試みましたが、運がありませんでした。アップロードディレクトリに作成されたファイルは壊れており、元のファイル(他の場所に保存されている)と比べてサイズがわずかです。
ウェストマーク、

UPDATEが機能しない
AmiNadimi 2017

回答:


166

ウェブから画像を取得してモデルに保存するコードがあります。重要なビットは次のとおりです。

from django.core.files import File  # you need this somewhere
import urllib


# The following actually resides in a method of my model

result = urllib.urlretrieve(image_url) # image_url is a URL to an image

# self.photo is the ImageField
self.photo.save(
    os.path.basename(self.url),
    File(open(result[0], 'rb'))
    )

self.save()

私のモデルから引き出されており、コンテキストから少し引き出されているため、少し混乱していますが、重要な部分は次のとおりです。

  • Webからプルされた画像は、upload_toフォルダーに保存されません。代わりに、urllib.urlretrieve()によって一時ファイルとして保存され、後で破棄されます。
  • ImageField.save()メソッドは、ファイル名(os.path.basenameビット)とdjango.core.files.Fileオブジェクトを受け取ります。

ご不明な点がある場合や説明が必要な場合はお知らせください。

編集:わかりやすくするために、ここにモデルを示します(必要なインポート文はすべて差し引いてあります)。

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

2
tvon-私はこの効果に対して何かを試みましたが、多分私はそれをもう一度やるつもりです、実際、私はこれに非常に似ているコードを持っていました。(たとえそれが文脈外であっても、それがどのように機能するかを見ることができます)。
T.ストーン

2
画像にURLパラマターガンクが添付されないように、URL解析も使用することをお勧めします。import urlparseos.path.basename(urlparse.urlparse(self.url).path)。投稿してくれてありがとう、役に立ちました。
dennmat

1
django.core.exceptions.SuspiciousOperation: '/images/10.jpg'へのアクセスの試みが拒否されました。
DataGreed 2012

2
@DataGreedでは、モデルのupload_to定義からスラッシュ「/」を削除する必要があります。これはここで扱われました
tsikov 2012

次のようなエラーが発生しますprohibited to prevent data loss due to unsaved related object 'stream'.
。– Dipak

95

モデルがまだ作成されていない場合は、非常に簡単です。

最初に、画像ファイルをアップロードパスにコピーします(次のスニペットでは、「path /」と想定しています)。

次に次のようなものを使用します。

class Layout(models.Model):
    image = models.ImageField('img', upload_to='path/')

layout = Layout()
layout.image = "path/image.png"
layout.save()

テスト済みでdjango 1.4で動作していますが、既存のモデルでも動作する可能性があります。


10
これは正解です、もっと投票が必要です!!! このソリューションもここで見つかりました。
Andrew Swihart 2013年

こんにちは。質問があります。Amazon S3バックエンドでdjango-storagesを使用しています。これにより新しいアップロードがトリガーされますか?
Salvatore Iovene 2013

OPは「Djangoにファイルの再保存を試行させることなく」と尋ね、これがその答えです!
frnhr 2014年

2
Djangoには、ディスク上のファイル名の重複を考慮するための既存のロジックがあります。ユーザーがファイル名の重複をチェックする必要があるため、このメソッドはそのロジックを壊します。
Chris Conlan、

1
@Conlan:ファイル名にGUIDを追加します。
Rabih Kodeih 2018年

41

ほんの少しの発言。tvon answerは機能しますが、Windowsで作業している場合は、おそらくでopen()ファイルにしたいと思うでしょう'rb'。このような:

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

または、最初の0x1Aバイトでファイルが切り捨てられます。


1
おかげで、私はこのような低レベルの詳細ウィンドウが直面することを忘れがちです。
mike_k

fml ... Linuxマシンでそのパラメーターが渡されるとどうなりますか?
DMac the Destroyer

1
私自身の質問に答えました...スパムを申し訳ありません。これに関するいくつかのドキュメントがここにありました。「Unixでは、モードに「b」を追加しても問題ありません。そのため、プラットフォームに依存せずにすべてのバイナリファイルに使用できます。」
DMac the Destroyer 2011

16

以下は、適切に機能し、ファイルを特定の形式に変換できる方法です(「モードPをJPEGとして書き込めない」エラーを回避するため)。

import urllib2
from django.core.files.base import ContentFile
from PIL import Image
from StringIO import StringIO

def download_image(name, image, url):
    input_file = StringIO(urllib2.urlopen(url).read())
    output_file = StringIO()
    img = Image.open(input_file)
    if img.mode != "RGB":
        img = img.convert("RGB")
    img.save(output_file, "JPEG")
    image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False)

ここで、imageはdjango ImageFieldまたはyour_model_instance.imageです。使用例は次のとおりです。

p = ProfilePhoto(user=user)
download_image(str(user.id), p.image, image_url)
p.save()

お役に立てれば


12

OK、あなたがする必要があるすべてが既存の画像ファイルパスをImageFieldに関連付けることであるなら、この解決策は役に立つかもしれません:

from django.core.files.base import ContentFile

with open('/path/to/already/existing/file') as f:
  data = f.read()

# obj.image is the ImageField
obj.image.save('imgfilename.jpg', ContentFile(data))

まあ、本格的な場合、既存の画像ファイルはImageFieldに関連付けられませんが、このファイルのコピーはupload_to dirに「imgfilename.jpg」として作成され、ImageFieldに関連付けられます。


2
バイナリファイルとして開いていませんか?
Mariusz Jamro 2016

@MariuszJamroが言ったと同じように、それは次のようにする必要があります:with open('/path/to/already/existing/file', 'rb') as f:
rahmatns

また、オブジェクトを保存することを忘れないでください:obj.save()
rahmatns

11

私がしたことは、ファイルをディスクに保存しないだけの独自のストレージを作成することでした:

from django.core.files.storage import FileSystemStorage

class CustomStorage(FileSystemStorage):

    def _open(self, name, mode='rb'):
        return File(open(self.path(name), mode))

    def _save(self, name, content):
        # here, you should implement how the file is to be saved
        # like on other machines or something, and return the name of the file.
        # In our case, we just return the name, and disable any kind of save
        return name

    def get_available_name(self, name):
        return name

次に、私のモデルでは、ImageFieldに新しいカスタムストレージを使用しました。

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')

7

ファイルをロードして再保存するオーバーヘッドを発生させずに(!!)、実際のファイル名を "設定"したい場合、または文字フィールド(!!!)を使用したい場合は、次のような方法を試してください。 -

model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg')

これにより、ファイルを実際にアップロードした場合と同じように、model_instance.myfile.urlと残りのすべてがライトアップされます。

@ t-stoneが言うように、私たちが本当に望んでいるのは、instance.myfile.path = 'my-filename.jpg'を設定できるようにすることですが、Djangoは現在これをサポートしていません。


model_instanceがファイルを含むモデルのインスタンスである場合、他の「インスタンス」は何を表していますか?
h3。

7

私の考えでは最も簡単な解決策:

from django.core.files import File

with open('path_to_file', 'r') as f:   # use 'rb' mode for python3
    data = File(f)
    model.image.save('filename', data, True)

3

これらの回答の多くは時代遅れであり、私は何時間も欲求不満に費やしました(私は一般的にDjangoとWeb開発者にはかなり新しいです)。しかし、私は@iambibhasによってこの優れた要点を見つけました:https ://gist.github.com/iambibhas/5051911

import requests

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile


def save_image_from_url(model, url):
    r = requests.get(url)

    img_temp = NamedTemporaryFile(delete=True)
    img_temp.write(r.content)
    img_temp.flush()

    model.image.save("image.jpg", File(img_temp), save=True)

2

これはあなたが探している答えではないかもしれません。ただし、ImageFileの代わりにcharfieldを使用してファイルのパスを保存できます。このようにして、ファイルを再作成せずに、プログラムでアップロードされた画像をフィールドに関連付けることができます。


ええ、私はこれをあきらめたくて、MySQLに直接書き込むか、CharField()を使用したくなりました。
T.ストーン

1

あなたが試すことができます:

model.ImageField.path = os.path.join('/Upload', generated_image_path)

1
class tweet_photos(models.Model):
upload_path='absolute path'
image=models.ImageField(upload_to=upload_path)
image_url = models.URLField(null=True, blank=True)
def save(self, *args, **kwargs):
    if self.image_url:
        import urllib, os
        from urlparse import urlparse
        file_save_dir = self.upload_path
        filename = urlparse(self.image_url).path.split('/')[-1]
        urllib.urlretrieve(self.image_url, os.path.join(file_save_dir, filename))
        self.image = os.path.join(file_save_dir, filename)
        self.image_url = ''
    super(tweet_photos, self).save()

1
class Pin(models.Model):
    """Pin Class"""
    image_link = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='images/', blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    source_name = models.CharField(max_length=255, null=True, blank=True)
    source_link = models.CharField(max_length=255, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    tags = models.ForeignKey(Tag, blank=True, null=True)

    def __unicode__(self):
        """Unicode class."""
        return unicode(self.image_link)

    def save(self, *args, **kwargs):
        """Store image locally if we have a URL"""
        if self.image_link and not self.image:
            result = urllib.urlretrieve(self.image_link)
            self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r')))
            self.save()
            super(Pin, self).save()

1

ワーキング!FileSystemStorageを使用して画像を保存できます。以下の例を確認してください

def upload_pic(request):
if request.method == 'POST' and request.FILES['photo']:
    photo = request.FILES['photo']
    name = request.FILES['photo'].name
    fs = FileSystemStorage()
##### you can update file saving location too by adding line below #####
    fs.base_location = fs.base_location+'/company_coverphotos'
##################
    filename = fs.save(name, photo)
    uploaded_file_url = fs.url(filename)+'/company_coverphotos'
    Profile.objects.filter(user=request.user).update(photo=photo)

Nids、このソリューションを完全に機能させていただきありがとうございます。あなたは私の多くの時間を節約しました:)
Mehmet Burak Ibis

0

あなたが使用することができますジャンゴRESTフレームワークとのpython の要求のジャンゴのImageFieldにイメージを保存するプログラムにライブラリを

次に例を示します。

import requests


def upload_image():
    # PATH TO DJANGO REST API
    url = "http://127.0.0.1:8080/api/gallery/"

    # MODEL FIELDS DATA
    data = {'first_name': "Rajiv", 'last_name': "Sharma"}

    #  UPLOAD FILES THROUGH REST API
    photo = open('/path/to/photo'), 'rb')
    resume = open('/path/to/resume'), 'rb')
    files = {'photo': photo, 'resume': resume}

    request = requests.post(url, data=data, files=files)
    print(request.status_code, request.reason) 

0

Django 3では、次のようなモデルがあります。

class Item(models.Model):
   name = models.CharField(max_length=255, unique=True)
   photo= models.ImageField(upload_to='image_folder/', blank=True)

画像がすでにアップロードされている場合は、直接行うことができます。

Item.objects.filter(...).update(photo='image_folder/sample_photo.png')

または

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.