使用Django模型在数据库中存储图像[英] Storing Images In DB Using Django Models

本文是小编为大家收集整理的关于使用Django模型在数据库中存储图像的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在使用Django创建一个Web服务,我希望该Web服务返回图像.我正在决定我的Web服务的基本体系结构.我在Google上绊倒后得出的结论是:

  1. 我应该将图像编码为base64格式后将图像存储在db中.
  2. 当直接碱基64解码字符串传输时,传输图像很容易.

但是我有一个问题如何使用django型号将bases64编码字符串存储在DB中?另外,如果您在我的基本体系结构中看到任何缺陷,请引导我.

我是Web Services和Django的新手

谢谢!

推荐答案

有些人指出,将DB用于存储图像不是一个好主意,但这不是真的.我的建议是将Django与 appengine blobstore Service 通过这种方式:

首先,创建一个 django自定义存储(或从其他人那里挑选一个,例如这个):

from django.core.files.storage import Storage 

class AppEngineBlobStorage(Storage):

    def exists(self, name):
       ...   

    def size(self, name):
       ...

    def url(self, name):
       ...

    def delete(self, name):
       ...

    def listdir(self, path):
       raise NotImplementedError()

此自定义存储可以接收Django映像,将它们转换为Base64字符串,然后将其发送到您的Appengine BlobStore服务应用程序(例如,通过XMLRPC).

然后,创建一个django映像模型:

from django.db import models
from django.db.models.fields.files import ImageField
from .storage import AppEngineBlobStorage

class Photo(models.Model):
    caption = models.CharField(max_length=64, blank=True)
    blob = ImageField(
        upload_to='BlobStorage',
        storage=AppEngineBlobStorage(),
        max_length=255,
        blank=False,
    )
    serving_url = models.URLField()
    ...

然后,您必须创建一个申请应用程序,以接收DJANGO请求以存储图像,将base64字符串转换为RAW并将其存储在斑点中.例如:

# coding=utf-8

from __future__ import with_statement

import webapp2

from base64 import b64decode
from StringIO import StringIO
from xmlrpcserver import XmlRpcServer

from google.appengine.api import files
from google.appengine.api import images
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers


class ImageRpcServer(object):

    def upload(self, meta, photo, mime_type):
        data = b64decode(photo)
        file_name = files.blobstore.create(mime_type=mime_type)
        with files.open(file_name, 'a') as f:
            f.write(data)
        files.finalize(file_name)
        key = files.blobstore.get_blob_key(file_name)
        return str(key)

    def serving_url(self, meta, key):
        return images.get_serving_url(blobstore.BlobKey(key))

      ...

服务斑点

关键是函数 get_serving_url .来自Google文档:

如果您正在使用图像,则更有效,可能 不太昂贵的方法是使用app Engine使用get_serving_url 图像API而不是send_blob. get_serving_url函数让 您直接提供图像,而无需通过您的应用程序 发动机实例.

最后,通过将图像与Appengine一起使用,您可以使用调整大小和裁剪图像的出色功能(检查get_serving_url函数的文档),例如:

//将图像大小调整到32个像素(保留方面比例) http://your_app_id.app_id.appsspot.com/randomstromstringimageImageImageImageImageImageImageImageid=s32

希望它有帮助.祝你好运!

其他推荐答案

您可以将图像存储在 bytea字段中.

bytea数据类型允许存储二进制字符串. postgres documention link

django的较早版本不支持bytea字段,因此我一直在使用此库,称为 djorm -ext-pgbytea :

其他推荐答案

我这样做:

我的数据库是Postgres,我通常会忽略Djago迁移,因此我更有灵活性地在数据库方面做事. 我确实使用适当的触发器创建视图来编码和解码数据库端的图像,因此:

为此表:

CREATE TABLE sales.brand (
   id serial not null, 
   brand_name character varying(100), 
   is_national_brand boolean default true not null, 
   general_comments text, 
   logo_image bytea
   CONSTRAINT brand_pk PRIMARY KEY (id)

)

我创建这样的视图:

CREATE OR REPLACE VIEW sales.v_brand_base64 AS 
SELECT brand.id,
    brand.brand_name,
    brand.is_national_brand,
    brand.general_comments,
    logo_image,
    encode(brand.logo_image, 'base64'::text) AS base64_logo_image
FROM sales.brand;

要使您需要更新的视图,您需要创建而不是触发器:

on Update触发器的示例:

CREATE OR REPLACE FUNCTION sales.brand_view_update()
   RETURNS trigger
   LANGUAGE plpgsql
AS 
$BODY$
BEGIN

    UPDATE sales.brand SET 
        id = NEW.id, 
        brand_name = NEW.brand_name, 
        is_national_brand = NEW.is_national_brand, 
        general_comments = NEW.general_comments,
        logo_image = decode(NEW.base64_logo_image, 'base64')
    WHERE     
        id = OLD.id;
    
    RETURN NEW;
END;
$BODY$
VOLATILE
SECURITY INVOKER
CALLED ON NULL INPUT
COST 100;

CREATE TRIGGER do_brand_update INSTEAD OF UPDATE
ON sales.v_brand_base64
FOR EACH ROW 
EXECUTE PROCEDUE sales.brand_view_update();

在django方面:

模型:

class Brand(models.Model):
    id = models.AutoField(primary_key=True)
    brand_name = models.CharField(max_length=100)
    is_national_brand = models.BooleanField()
    general_comments = models.TextField(blank=True, null=True)
    logo_image = models.ImageField(null=True)
    base64_logo_image = models.TextField(blank=True, null=True)

    def b64_logo_image(self):
        base64enc = self.base64_logo_image
        return format_html('<img style="width: 100%" display: block;" src="data:image/bmp;base64,{}">', base64enc)

    def __str__(self):
        return self.brand_name

    @property
    def detail_fields(self):
        # (0:value, 1:href, 2:header, 3:size, 4:list, 5:detail, )
        return (
            (self.brand_name, safe_self_rev(self, 'brand_detail'), 'Brand Name', 10, True, True, ),
            (self.is_national_brand, None, 'National Brand', 5, True, True, ),
            (self.general_comments, None, 'Comments', 5, True, True, ),
            (self.b64_logo_image, None, 'Logo', 5, True, True, ),
        )

    class Meta:
        managed = False
        db_table = '"sales"."v_brand_base64"'

和视图如下:

class BrandUpdateView(UpdateView):

    model = Brand
    fields = ['brand_name', 'is_national_brand', 'general_comments', 'logo_image', ]

    template_name = "sales/brand_edit.html"

    def get_success_url(self):
        if self.object.id is None:
            return reverse('index')
        else:
            return reverse('brand_detail', args=[self.object.id])

    def form_valid(self, form):
        if form.is_valid():
            brand = form.save(commit=False)
            logo_image = form.cleaned_data['logo_image'].file.read()
            brand.logo_image = None
            logo_base64 = base64.encodebytes(logo_image).decode();
            brand.base64_logo_image = logo_base64
            brand.save()
        return super().form_valid(form)

这可能不是最好的方法,但它起作用

图像保存在bytea字段中的数据库上,您可以像字段一样使用属性b64_logo_image将图像呈现在模板上 就像是 {{object.b64_logo_image}}

即将在数据库中记录图像,这就是为什么BLOB和BYTEA字段存在. 这是您的选择.

我使用做,没有问题.正如我所说的,我是一个数据库侧的家伙:)

本文地址:https://www.itbaoku.cn/post/597755.html

问题描述

I am using Django for creating one Web service and I want that web service to return Images. I am deciding the basic architecture of my Web service. What I came up to conclusion after stumbling on google is:

  1. I should store Images in DB after encoding them to Base64 format.
  2. Transferring the Images would be easy when directly Bases64 decoded string is transmitted.

But I have one issue how can I store bases64 encoded string in DB using Django models? Also, if you see any flaw in my basic architecture please guide me.

I am new to Web services and Django

Thanks!!

推荐答案

Some people states that using DB for storing images is not a good idea but that's no true. My advice is to use Django with AppEngine Blobstore Service in this way:

First, create a Django Custom Storage (or pick one from someone else like this one):

from django.core.files.storage import Storage 

class AppEngineBlobStorage(Storage):

    def exists(self, name):
       ...   

    def size(self, name):
       ...

    def url(self, name):
       ...

    def delete(self, name):
       ...

    def listdir(self, path):
       raise NotImplementedError()

This custom storage can receive Django images, convert them to Base64 strings and send them to your AppEngine Blobstore Service application (via xmlrpc for example).

Then, create a Django Image model:

from django.db import models
from django.db.models.fields.files import ImageField
from .storage import AppEngineBlobStorage

class Photo(models.Model):
    caption = models.CharField(max_length=64, blank=True)
    blob = ImageField(
        upload_to='BlobStorage',
        storage=AppEngineBlobStorage(),
        max_length=255,
        blank=False,
    )
    serving_url = models.URLField()
    ...

Then, you have to create an AppEngine application for receiving Django requests for storing images, transform Base64 strings to raw and store them in a Blob. For example:

# coding=utf-8

from __future__ import with_statement

import webapp2

from base64 import b64decode
from StringIO import StringIO
from xmlrpcserver import XmlRpcServer

from google.appengine.api import files
from google.appengine.api import images
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers


class ImageRpcServer(object):

    def upload(self, meta, photo, mime_type):
        data = b64decode(photo)
        file_name = files.blobstore.create(mime_type=mime_type)
        with files.open(file_name, 'a') as f:
            f.write(data)
        files.finalize(file_name)
        key = files.blobstore.get_blob_key(file_name)
        return str(key)

    def serving_url(self, meta, key):
        return images.get_serving_url(blobstore.BlobKey(key))

      ...

Serving a blob

The key is the function get_serving_url. From Google docs:

If you are serving images, a more efficient and potentially less-expensive method is to use get_serving_url using the App Engine Images API rather than send_blob. The get_serving_url function lets you serve the image directly, without having to go through your App Engine instances.

Finally, by serving images with AppEngine, you can use the awesome feature of resize and crop images (check the documentation of get_serving_url function), for example:

// Resize the image to 32 pixels (aspect-ratio preserved) http://your_app_id.appspot.com/randomStringImageId=s32

Hope it helps. Good luck!

其他推荐答案

You can store image in bytea field.

The bytea data type allows storage of binary strings. Postgres Documentation Link

The earlier version of Django does not support bytea field, so I have been using this library called djorm-ext-pgbytea:

其他推荐答案

I do it like this:

My database is postgres and I usualy ignore djago migrations so I have more flexibility to do things on the database side. I do creating a view with the proper triggers to encode and decode the image at the database side so for example:

for this table:

CREATE TABLE sales.brand (
   id serial not null, 
   brand_name character varying(100), 
   is_national_brand boolean default true not null, 
   general_comments text, 
   logo_image bytea
   CONSTRAINT brand_pk PRIMARY KEY (id)

)

I create a view like this one:

CREATE OR REPLACE VIEW sales.v_brand_base64 AS 
SELECT brand.id,
    brand.brand_name,
    brand.is_national_brand,
    brand.general_comments,
    logo_image,
    encode(brand.logo_image, 'base64'::text) AS base64_logo_image
FROM sales.brand;

To make the view updatable of coarse you need to create the instead of triggers:

example of the on update trigger:

CREATE OR REPLACE FUNCTION sales.brand_view_update()
   RETURNS trigger
   LANGUAGE plpgsql
AS 
$BODY$
BEGIN

    UPDATE sales.brand SET 
        id = NEW.id, 
        brand_name = NEW.brand_name, 
        is_national_brand = NEW.is_national_brand, 
        general_comments = NEW.general_comments,
        logo_image = decode(NEW.base64_logo_image, 'base64')
    WHERE     
        id = OLD.id;
    
    RETURN NEW;
END;
$BODY$
VOLATILE
SECURITY INVOKER
CALLED ON NULL INPUT
COST 100;

and

CREATE TRIGGER do_brand_update INSTEAD OF UPDATE
ON sales.v_brand_base64
FOR EACH ROW 
EXECUTE PROCEDUE sales.brand_view_update();

At django side:

the model:

class Brand(models.Model):
    id = models.AutoField(primary_key=True)
    brand_name = models.CharField(max_length=100)
    is_national_brand = models.BooleanField()
    general_comments = models.TextField(blank=True, null=True)
    logo_image = models.ImageField(null=True)
    base64_logo_image = models.TextField(blank=True, null=True)

    def b64_logo_image(self):
        base64enc = self.base64_logo_image
        return format_html('<img style="width: 100%" display: block;" src="data:image/bmp;base64,{}">', base64enc)

    def __str__(self):
        return self.brand_name

    @property
    def detail_fields(self):
        # (0:value, 1:href, 2:header, 3:size, 4:list, 5:detail, )
        return (
            (self.brand_name, safe_self_rev(self, 'brand_detail'), 'Brand Name', 10, True, True, ),
            (self.is_national_brand, None, 'National Brand', 5, True, True, ),
            (self.general_comments, None, 'Comments', 5, True, True, ),
            (self.b64_logo_image, None, 'Logo', 5, True, True, ),
        )

    class Meta:
        managed = False
        db_table = '"sales"."v_brand_base64"'

And the view like:

class BrandUpdateView(UpdateView):

    model = Brand
    fields = ['brand_name', 'is_national_brand', 'general_comments', 'logo_image', ]

    template_name = "sales/brand_edit.html"

    def get_success_url(self):
        if self.object.id is None:
            return reverse('index')
        else:
            return reverse('brand_detail', args=[self.object.id])

    def form_valid(self, form):
        if form.is_valid():
            brand = form.save(commit=False)
            logo_image = form.cleaned_data['logo_image'].file.read()
            brand.logo_image = None
            logo_base64 = base64.encodebytes(logo_image).decode();
            brand.base64_logo_image = logo_base64
            brand.save()
        return super().form_valid(form)

It is probably the not best way but it works

the image is saved on the database in a bytea field and you can use the property b64_logo_image like a field to render the image on your templates something like {{object.b64_logo_image}}

About to record images at database that is why blob and bytea fields exists. It is your choice.

I use do do it and I have no problems. As I said I am more a database side guy :)