Django: resim url'den bir ImageField'a resim ekle


86

çirkin ingilizcem için lütfen beni affedin ;-)

Bu çok basit modeli hayal edin:

class Photo(models.Model):
    image = models.ImageField('Label', upload_to='path/')

Bir resim URL'sinden bir Fotoğraf oluşturmak istiyorum (yani, django yönetici sitesinde elle değil).

Bunun gibi bir şey yapmam gerektiğini düşünüyorum:

from myapp.models import Photo
import urllib

img_url = 'http://www.site.com/image.jpg'
img = urllib.urlopen(img_url)
# Here I need to retrieve the image (as the same way that if I put it in an input from admin site)
photo = Photo.objects.create(image=image)

Umarım bana söylemezseniz sorunu iyi açıkladım.

Teşekkür ederim :)

Düzenle :

Bu işe yarayabilir ancak contentbir django Dosyasına nasıl dönüştürüleceğini bilmiyorum :

from urlparse import urlparse
import urllib2
from django.core.files import File

photo = Photo()
img_url = 'http://i.ytimg.com/vi/GPpN5YUNDeI/default.jpg'
name = urlparse(img_url).path.split('/')[-1]
content = urllib2.urlopen(img_url).read()

# problem: content must be an instance of File
photo.image.save(name, content, save=True)

Yanıtlar:


96

Aynı sorun için http://www.djangosnippets.org/snippets/1890/ oluşturdum . Urllib.urlretrieve varsayılan olarak herhangi bir hata işleme yapmadığından, kodun özsüz yanıtına benzerdir, ancak urllib2.urlopen kullanır, bu nedenle ihtiyacınız olan şey yerine 404/500 sayfasının içeriğini elde etmek kolaydır. Geri arama işlevi ve özel URLOpener alt sınıfı oluşturabilirsiniz, ancak bunun gibi kendi geçici dosyamı oluşturmayı daha kolay buldum:

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

img_temp = NamedTemporaryFile(delete=True)
img_temp.write(urllib2.urlopen(url).read())
img_temp.flush()

im.file.save(img_filename, File(img_temp))

3
son satır ne yapıyor? ne imgelen nesne?
priestc

1
@priestc: imbiraz kısaydı - bu örnekte, immodel fileörneğiydi ve o örnekteki bir FileField / ImageField'ın hayal gücü olmayan adıydı. Buradaki asıl API belgeleri önemlidir - bu teknik, bir nesneye bağlı bir Django File nesnesine sahip olduğunuz her yerde çalışmalıdır: docs.djangoproject.com/en/1.5/ref/files/file/…
Chris Adams

27
Geçici bir dosya gereksizdir. Sizin requestsyerine kullanarak urllib2şunları yapabilirsiniz: image_content = ContentFile(requests.get(url_image).content)ve sonra obj.my_image.save("foo.jpg", image_content).
Stan

Stan: istekler bunu basitleştirir, ancak IIRC, hatalar veya eksik yanıtlarla karışıklığı önlemek için ilk olarak yükseltmenin bir sorun olacağını söylemezseniz, ilk olarak yükseltmenin bir sorun olacağını söylemez
Chris Adams

İlk olarak Django 1.1 / 1.2 döneminde yazıldığı için bunu modernize etmek muhtemelen iyi bir fikir olacaktır. Bununla birlikte, ContentFile'ın hala tüm dosyayı belleğe yükleme sorunu yaşadığına inanıyorum, bu nedenle güzel bir optimizasyon makul bir yığın boyutuyla iter_content kullanmak olurdu.
Chris Adams

32

from myapp.models import Photo
import urllib
from urlparse import urlparse
from django.core.files import File

img_url = 'http://www.site.com/image.jpg'

photo = Photo()    # set any other fields, but don't commit to DB (ie. don't save())
name = urlparse(img_url).path.split('/')[-1]
content = urllib.urlretrieve(img_url)

# See also: http://docs.djangoproject.com/en/dev/ref/files/file/
photo.image.save(name, File(open(content[0])), save=True)


Merhaba, bana yardım ettiğiniz için teşekkür ederim;). Sorun şudur (belgeden alıntı yapıyorum): "İçerik bağımsız değişkeninin File veya File alt sınıfının bir örneği olması gerektiğine dikkat edin." Peki, içeriğinizle Dosya örneği oluşturmak için herhangi bir çözümünüz var mı?

Yeni düzenlemeyi kontrol edin; bu şimdi çalışan bir örnek olmalı (test
etmemiş olsam

Başka alanların da olduğu modelim ne olacak? Url vb. Gibi. İd model.image.save (...) yaparsanız. diğer alanları nasıl kaydederim? Boş olamazlar DÜZENLEME: woudl böyle bir şey olabilir mi? >>> car.photo.save ('fotoğrafım.jpg', içindekiler, kaydet = Yanlış) >>> car.save ()
Harry

self.url ?? ... burada self.url nedir?
DeadDjangoDjoker

@DeadDjangoDjoker Fikrim yok, cevabımı düzenleyen kişiye sormanız gerekir. Bu cevap bu noktada 5 yaşında; Gelecek kuşaklar için önceki "çalışan" çözüme geri döndüm, ama dürüst olmak gerekirse, Chris Adam'ın cevabıyla daha iyisiniz.
özsüz

13

Chris Adams ve Stan'in söylediklerini birleştirerek ve bazı şeyleri Python 3 üzerinde çalışacak şekilde güncelleyerek, Requests'i yüklerseniz , şöyle bir şey yapabilirsiniz:

from urllib.parse import urlparse
import requests
from django.core.files.base import ContentFile
from myapp.models import Photo

img_url = 'http://www.example.com/image.jpg'
name = urlparse(img_url).path.split('/')[-1]

photo = Photo() # set any other fields, but don't commit to DB (ie. don't save())

response = requests.get(img_url)
if response.status_code == 200:
    photo.image.save(name, ContentFile(response.content), save=True)

Django'nun ContentFile belgeleri ve Requests dosya indirme örneğindeki daha ilgili belgeler .


5

ImageFieldsadece bir dizedir, MEDIA_ROOTayarınıza göre bir yoldur . Dosyayı kaydedin (bir görüntü olup olmadığını kontrol etmek için PIL kullanmak isteyebilirsiniz) ve alanı dosya adıyla doldurun.

Dolayısıyla, kodunuzun çıktısını urllib.urlopendosyanıza (medya konumunuzun içinde) kaydetmeniz, yolu hesaplamanız ve bunu modelinize kaydetmeniz gerektiğinden farklıdır .


5

Python 2'de basit uyarlamalarla çalışması gereken Python 3'te bu şekilde yapıyorum. Bu, aldığım dosyaların küçük olduğu bilgisine dayanıyor. Sizinki değilse, muhtemelen yanıtı belleğe almak yerine bir dosyaya yazmanızı öneririm.

BytesIO gereklidir çünkü Django dosya nesnesinde seek () öğesini çağırır ve urlopen yanıtları aramayı desteklemez. Bunun yerine read () tarafından döndürülen bayt nesnesini Django'nun ContentFile dosyasına iletebilirsiniz.

from io import BytesIO
from urllib.request import urlopen

from django.core.files import File


# url, filename, model_instance assumed to be provided
response = urlopen(url)
io = BytesIO(response.read())
model_instance.image_field.save(filename, File(io))

File (io) <File: None>
Susaj S Nair

@SusajSNair Bunun nedeni dosyanın bir adı olmaması ve bunun için kullanılan __repr__yöntemin Fileadı yazmasıdır. Tercih ederseniz name, Filenesneyi oluşturduktan sonra nesnede özniteliği ayarlayabilirsiniz File(io), ancak benim deneyimlerime göre önemli değil (çıktısını alırsanız daha güzel görünmesi dışında). ymmv.
jbg

3

Son zamanlarda aşağıdaki yaklaşımı python 3 ve Django 3'te kullanıyorum, belki bu diğerleri için de ilginç olabilir. Chris Adams çözümüne benziyor ama benim için artık işe yaramadı.

import urllib.request
from django.core.files.uploadedfile import SimpleUploadedFile
from urllib.parse import urlparse

from demoapp import models


img_url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png'
basename = urlparse(img_url).path.split('/')[-1]
tmpfile, _ = urllib.request.urlretrieve(img_url)

new_image = models.ModelWithImageOrFileField()
new_image.title = 'Foo bar'
new_image.file = SimpleUploadedFile(basename, open(tmpfile, "rb").read())
new_image.save()

Benim için çalışan tek çözüm budur (Python 3 + Django 2). Sadece biraz önemsiz bir yorum, temel adının URL'ye bağlı olarak her durumda doğru uzantıya sahip olmayabileceğidir.
fizikAI

2

Geçici bir dosya oluşturmanız gerekmediğini yeni keşfettim:

Url içeriğini doğrudan django'dan minio'ya aktarın

Dosyalarımı minio'da depolamalıyım ve çok fazla disk alanı olmayan django docker konteynerlerine sahip olmalıyım ve büyük video dosyalarını indirmem gerekiyor, bu yüzden bu bana gerçekten yardımcı oldu.


-5

bu doğru ve çalışma yolu

class Product(models.Model):
    upload_path = 'media/product'
    image = models.ImageField(upload_to=upload_path, null=True, blank=True)
    image_url = models.URLField(null=True, blank=True)

    def save(self, *args, **kwargs):
        if self.image_url:
            import urllib, os
            from urlparse import urlparse
            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(upload_path, filename)
            self.image_url = ''
            super(Product, self).save()

14
Bu doğru yol olamaz, FielField'ın tüm dosya depolama mekanizmasını aşarsınız ve depolama api'sine saygı göstermezsiniz.
Andre Bossard
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.