por favor me desculpe pelo meu inglês feio ;-)
Imagine este modelo muito simples:
class Photo(models.Model):
image = models.ImageField('Label', upload_to='path/')
Eu gostaria de criar uma foto a partir de um URL de imagem (ou seja, não manualmente no site de administração do django).
Acho que preciso fazer algo assim:
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)
Espero ter explicado bem o problema, se não me diga.
Obrigado :)
Editar:
Isso pode funcionar, mas não sei como converter content
para um arquivo Django:
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)
im
objeto?im
foi um pouco conciso - naquele exemplo,im
era a instância do modelo efile
era o nome sem imaginação de um FileField / ImageField naquela instância. A documentação da API atual é o que importa - essa técnica deve funcionar em qualquer lugar onde você tenha um objeto Django File vinculado a um objeto: docs.djangoproject.com/en/1.5/ref/files/file/…requests
vez deurllib2
você pode fazer:image_content = ContentFile(requests.get(url_image).content)
e entãoobj.my_image.save("foo.jpg", image_content)
.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)
fonte
Combinando o que Chris Adams e Stan disseram e atualizando as coisas para funcionar no Python 3, se você instalar Requests, poderá fazer algo assim:
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)
Documentos mais relevantes na documentação ContentFile do Django e exemplo de download de arquivo Requests .
fonte
ImageField
é apenas uma string, um caminho relativo à suaMEDIA_ROOT
configuração. Basta salvar o arquivo (você pode usar PIL para verificar se é uma imagem) e preencher o campo com o nome do arquivo.Portanto, ele difere do seu código porque você precisa salvar a saída do seu
urllib.urlopen
arquivo em (dentro do local da mídia), descobrir o caminho e salvá-lo no seu modelo.fonte
Eu faço isso dessa forma no Python 3, que deve funcionar com adaptações simples no Python 2. Isso se baseia no meu conhecimento de que os arquivos que estou recuperando são pequenos. Se o seu não for, provavelmente recomendo gravar a resposta em um arquivo em vez de armazená-la em buffer na memória.
BytesIO é necessário porque o Django chama a busca () no objeto de arquivo e as respostas urlopen não suportam a busca. Você poderia passar o objeto bytes retornado por read () para ContentFile do Django.
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))
fonte
__repr__
método paraFile
grava o nome. Se preferir, você pode definir oname
atributo noFile
objeto depois de criá-loFile(io)
, mas, na minha experiência, isso não importa (além de torná-lo mais bonito se você imprimi-lo). ymmv.Recentemente, usei a seguinte abordagem em python 3 e Django 3, talvez isso também seja interessante para outros. É semelhante à solução de Chris Adams, mas para mim não funcionou mais.
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()
fonte
Acabei de descobrir que você não precisa gerar um arquivo temporário:
Transmita conteúdo de url diretamente do django para o minio
Eu tenho que armazenar meus arquivos em minio e ter contêineres docker django sem muito espaço em disco e preciso baixar arquivos de vídeo grandes, então isso foi realmente útil para mim.
fonte
esta é a maneira certa e funcionando
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()
fonte