简体   繁体   English

"以编程方式将图像保存到 Django ImageField"

[英]Programmatically saving image to Django ImageField

Ok, I've tried about near everything and I cannot get this to work.好的,我已经尝试了几乎所有的东西,但我无法让它工作。

I have some code that fetches an image off the web and stores it in a model.我有一些代码可以从网络上获取图像并将其存储在模型中。 The important bits are:重要的位是:

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()

That's a bit confusing because it's pulled out of my model and a bit out of context, but the important parts are:这有点令人困惑,因为它脱离了我的模型并且有点脱离了上下文,但重要的部分是:

  • The image pulled from the web is not stored in the upload_to folder, it is instead stored as a tempfile by urllib.urlretrieve() and later discarded.从网络拉取的图像没有存储在 upload_to 文件夹中,而是由 urllib.urlretrieve() 存储为临时文件,然后被丢弃。
  • The ImageField.save() method takes a filename (the os.path.basename bit) and a django.core.files.File object. ImageField.save() 方法接受一个文件名(os.path.basename 位)和一个 django.core.files.File 对象。

Let me know if you have questions or need clarification.如果您有任何疑问或需要澄清,请告诉我。

Edit: for the sake of clarity, here is the model (minus any required import statements):编辑:为了清楚起见,这里是模型(减去任何必需的导入语句):

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()

Super easy if model hasn't been created yet:如果模型尚未创建,则超级简单:

First , copy your image file to the upload path (assumed = 'path/' in following snippet).首先,将您的图像文件复制到上传路径(假设 = 'path/'在以下代码段中)。

Second , use something like:其次,使用类似的东西:

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

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

tested and working in django 1.4, it might work also for an existing model.在 django 1.4 中测试和工作,它可能也适用于现有模型。

Just a little remark.只是一点意见。 tvon answer works but, if you're working on windows, you probably want to open() the file with 'rb' . tvon answer 有效,但是,如果您在 Windows 上工作,您可能想用'rb' open()文件。 Like this:像这样:

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()

or you'll get your file truncated at the first 0x1A byte.否则你的文件会在第一个0x1A字节处被截断。

Here is a method that works well and allows you to convert the file to a certain format as well (to avoid "cannot write mode P as JPEG" error):这是一种效果很好的方法,它允许您也将文件转换为某种格式(以避免“无法将模式 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)

where image is the django ImageField or your_model_instance.image here is a usage example:其中 image 是 django ImageField 或 your_model_instance.image 这里是一个用法示例:

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

Hope this helps希望这可以帮助

Ok, If all you need to do is associate the already existing image file path with the ImageField, then this solution may be helpfull:好的,如果您需要做的只是将已经存在的图像文件路径与 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))

Well, if be earnest, the already existing image file will not be associated with the ImageField, but the copy of this file will be created in upload_to dir as 'imgfilename.jpg' and will be associated with the ImageField.好吧,如果认真的话,已经存在的图像文件不会与ImageField相关联,但是该文件的副本将在upload_to dir中创建为'imgfilename.jpg',并将与ImageField相关联。

What I did was to create my own storage that will just not save the file to the disk:我所做的是创建自己的存储,但不会将文件保存到磁盘:

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

Then, in my models, for my ImageField, I've used the new custom storage:然后,在我的模型中,对于我的 ImageField,我使用了新的自定义存储:

from custom_storage import CustomStorage

custom_store = CustomStorage()

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

Another possible way to do that:另一种可能的方法是:

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)

If you want to just "set" the actual filename, without incurring the overhead of loading and re-saving the file (!!), or resorting to using a charfield (!!!), you might want to try something like this --如果您只想“设置”实际的文件名,而不会产生加载和重新保存文件的开销 (!!),或者诉诸于使用字符字段 (!!!),您可能想尝试这样的事情 - ——

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

This will light up your model_instance.myfile.url and all the rest of them just as if you'd actually uploaded the file.这将点亮您的 model_instance.myfile.url 和所有其余文件,就像您实际上传了文件一样。

Like @t-stone says, what we really want, is to be able to set instance.myfile.path = 'my-filename.jpg', but Django doesn't currently support that.就像@t-stone 所说,我们真正想要的是能够设置 instance.myfile.path = 'my-filename.jpg',但 Django 目前不支持。

A lot of these answers were outdated, and I spent many hours in frustration (I'm fairly new to Django & web dev in general).很多这些答案已经过时了,我在沮丧中花了很多时间(总的来说,我对 Django 和 Web 开发者还是很陌生)。 However, I found this excellent gist by @iambibhas: https://gist.github.com/iambibhas/5051911但是,我发现了@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)

This is might not be the answer you are looking for.这可能不是您正在寻找的答案。 but you can use charfield to store the path of the file instead of ImageFile.但是您可以使用 charfield 来存储文件的路径而不是 ImageFile。 In that way you can programmatically associate uploaded image to field without recreating the file.通过这种方式,您可以以编程方式将上传的图像与字段相关联,而无需重新创建文件。

With Django 3, with a model such as this one:使用 Django 3,使用这样的模型:

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

if the image has already been uploaded, we can directly do :如果图片已经上传,我们可以直接执行:

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

or或者

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()
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()
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()

Working!在职的! You can save image by using FileSystemStorage.您可以使用 FileSystemStorage 保存图像。 check the example below检查下面的例子

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)

你可以试试:

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

Your can use Django REST framework and python Requests library to Programmatically saving image to Django ImageField您可以使用Django REST framework和 python Requests库以编程方式将图像保存到 Django ImageField

Here is a Example:这是一个例子:

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) 
class DemoImage(models.Model):
    title = models.TextField(max_length=255, blank=False)
    image = models.ImageField(blank=False, upload_to="images/DemoImages/")

import requests
import urllib.request
from django.core.files import File
url = "https://path/to/logo.jpg"

# Below 3 lines is to fake as browser agent 
# as many sites block urllib class suspecting to be bots
opener = urllib.request.build_opener()
opener.addheaders = [("User-agent", "Mozilla/5.0")]
urllib.request.install_opener(opener)

# Issue command to actually download and create temp img file in memory        
result = urllib.request.urlretrieve(url)

# DemoImage.objects.create(title="title", image=File(open(result[0], "rb"))) 
# ^^ This erroneously results in creating the file like 
# images/DemoImages/path/to/temp/dir/logo_image_file 
# as opposed to 
# images/DemoImages/logo_image_file

# Solution to get the file in images/DemoImages/
reopen = open(result[0], "rb") # Returns a BufferedReader object of the temp image
django_file = File(reopen)     # Create the file from the BufferedReader object 
demoimg = DemoImage()
demoimg.title = "title"
demoimg.image.save("logo.png", django_file, save=True)

This approach also triggers file upload to cloudinary/S3 if so configured如果这样配置,这种方法还会触发文件上传到 cloudinary/S3

So, if you have a model with an imagefield with an upload_to attribute set, such as:因此,如果您的模型具有带有 upload_to 属性集的图像字段,例如:

class Avatar(models.Model):
    image_file = models.ImageField(upload_to=user_directory_path_avatar)

then it is reasonably easy to change the image, at least in django 3.15.那么更改图像相当容易,至少在 django 3.15 中是这样。

In the view, when you process the image, you can obtain the image from:在视图中,当您处理图像时,您可以从以下位置获取图像:

self.request.FILES['avatar']

which is an instance of type InMemoryUploadedFile, as long as your html form has the enctype set and a field for avatar...这是 InMemoryUploadedFile 类型的实例,只要您的 html 表单设置了 enctype 和一个头像字段...

    <form method="post" class="avatarform" id="avatarform" action="{% url avatar_update_view' %}" enctype="multipart/form-data">
         {% csrf_token %}
         <input id="avatarUpload" class="d-none" type="file" name="avatar">
    </form>

Then, setting the new image in the view is as easy as the following (where profile is the profile model for the self.request.user)然后,在视图中设置新图像就像下面一样简单(其中 profile 是 self.request.user 的配置文件模型)

profile.avatar.image_file.save(self.request.FILES['avatar'].name, self.request.FILES['avatar'])

There is no need to save the profile.avatar, the image_field already saves, and into the correct location because of the 'upload_to' callback function.由于 'upload_to' 回调函数,不需要保存 profile.avatar,image_field 已经保存,并保存到正确的位置。

I save the image with uuid in django 2 python 3 because thats how django do it:我在 django 2 python 3 中使用 uuid 保存图像,因为这就是 django 的做法:

import uuid   
from django.core.files import File 
import urllib

httpUrl = "https://miimgeurl/image.jpg"
result = urllib.request.urlretrieve(httpUrl)            
mymodel.imagefield.save(os.path.basename(str(uuid.uuid4())+".jpg"),File(open(result[0], 'rb')))
mymodel.save()

if use admin.py you can solve by override ( doc on django<\/a> ):如果使用 admin.py 你可以通过覆盖来解决( django 上的文档<\/a>):

def save_model(self, request, obj, form, change):
    obj.image_data = bytes(obj.image_name.read())
    super().save_model(request, obj, form, change)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM