12👍
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!
0👍
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:
0👍
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 🙂
- Django template variable value to string literal comparison fails
- Django : Change default value for an extended model class
- Linking django and mysql containers using docker-compose
- Django 1.9: ProgrammingError: relation "users_user" does not exist
0👍
I think the best approach is to store the ‘main file’ in your media path of your project and save the address of file(path to the file) in your model. this way you dont need to convert….
- Custom user model in django
- ArrayField missing 1 required positional argument
- Celerybeat not executing periodic tasks
- Can't delete cache for specific entry in Django