I have following class definition in mongoengine orm:
import mongoengine as me
class Description(me.Document):
user = me.ReferenceField(User, required=True)
name = me.StringField(required=True, max_length=50)
caption = me.StringField(required=True, max_length=80)
description = me.StringField(required=True, max_length=100)
image = me.ImageField()
in my post method of my tornado web requesthandler:
from PIL import Image
def post(self, *args, **kwargs):
merchant = self._merchant
data = self._data
obj_data = {}
if merchant:
params = self.serialize() # I am getting params dict. NO Issues with this.
obj_data['name'] = params.get('title', None)
obj_data['description'] = params.get('description', None)
path = params.get('file_path', None)
image = Image.open(path)
print image # **
obj_data['image'] = image # this is also working fine.
obj_data['caption'] = params.get('caption', None)
obj_data['user'] = user
des = Description(**obj_data)
des.save()
print obj_data['image'] # **
print des.image # This is printing as <ImageGridFsProxy: None>
** print obj_data['image'] and print image are printing following:
<PIL.PngImagePlugin.PngImageFile image mode=1 size=290x290 at 0x7F83AE0E91B8>
but
des.image still remains None.
Please suggest me what is wrong here.
Thanks in advance to all.
You can not just put PIL objects into a field with obj.image = image that way. You must do:
des = Description()
des.image.put(open(params.get('file_path', None)))
des.save()
In other words, ImageField should be filled with file object after creating an instance by calling put method.
Related
I want to make my model call save_image() everytime user creates new Post model.
I tried to override save() method, but it causes infinite loop (because save_image() calls self.image.save(). ).
I tried disabling signals, but they did nothing.
I wanted to add something like #receiver(post_save, sender=Post) for the save_image, and remove override of save() (I don't even know if it will solve problem with infinite loop).
The main problem here is that, save_post() MUST be called after standard save (because it needs Post.pk, Post.video). If you have any ideas, please help me.
Here's my code:
class Post(BaseModel):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
# Other fields
video = models.FileField(upload_to='post_videos/', default='defaults/video.mp4')
image = models.ImageField(upload_to='post_images', default='defaults/post_image.png')
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
self.save_image()
#receiver(post_save, sender=Post)
def save_image(self):
# Setting self.image to some frame of Video
cap = cv2.VideoCapture(self.video.path)
video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) - 1
if cap.isOpened() and video_length > 0:
success, image = cap.read()
if success:
rescaled = (255.0 / image.max() * (image - image.min())).astype(np.uint8)
PILimg = Image.fromarray(rescaled)
filename = "postimage"+str(self.pk)+".png"
f = BytesIO()
try:
PILimg.save(f, format='png')
s = f.getvalue()
self.image.save(filename, ContentFile(s))
finally:
f.close()
I found it!
I had to add save=False to self.image.save(filename, ContentFile(s)). If you had similiar issue, you may also want to save whole model again (calling super(Post, self).save(*args, **kwargs) again).
I had a similar issue.. I was creating a Reservation Confirmation pdf object by catching a post_save signal from Reservation model and ended up in an infinite loop when saving the pdf.
Below is the correct version of the code. The bug was in the last line which previously was:
self.saved_file.save(File(file))
class ReservationConfrimation(models.Model):
reservation = models.OneToOneField(Reservation, on_delete=models.CASCADE)
saved_file = models.FileField(null=True, upload_to="confirmations/")
def save(self, *args, **kwargs):
self._create_pdf(self.reservation)
super().save(*args, **kwargs)
def _create_pdf(self, reservation):
# buffer = io.BytesIO()
file = io.BytesIO()
c = canvas.Canvas(file, pagesize=letter, bottomup=1, verbosity=0)
# w,h = letter
c.line(0, 700, 612, 700)
c.line(0, 100, 612, 100)
c.drawString(306, 690, f"RESERVATION: {reservation.reservation_number}")
c.save()
file.name = f"{reservation.reservation_number}.pdf"
self.saved_file = File(file)
I'm trying to generate a preview for an "overlay" config stored in a django model than will be applied later to other model. I have not much experience manipulating files with python... =(
Here is my code:
import io
from django.conf import settings
from django.db import models
from wand.image import Image
from PIL.ImageFile import ImageFile, Parser, Image as PilImage
class Overlay(models.Model):
RELATIVE_POSITIONS = (...)
SIZE_MODES = (...)
name = models.CharField(max_length=50)
source = models.FileField(upload_to='overlays/%Y/%m/%d')
sample = models.ImageField(upload_to='overlay_samples/%Y/%m/%d', blank=True)
px = models.SmallIntegerField(default=0)
py = models.SmallIntegerField(default=0)
position = models.CharField(max_length=2, choices=RELATIVE_POSITIONS)
width = models.SmallIntegerField(default=0)
height = models.SmallIntegerField(default=0)
size_mode = models.CharField(max_length=1, choices=SIZE_MODES, default='B')
last_edit = models.DateTimeField(auto_now=True)
def generate_sample(self):
"""
Generates the sample image and saves it in the "sample" field model
:return: void
"""
base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg'))
overlay_pic = Image(file=self.source)
result_pic = io.BytesIO()
pil_parser = Parser()
if self.width or self.height:
resize_args = {}
if self.width:
resize_args['width'] = self.width
if self.height:
resize_args['height'] = self.height
overlay_pic.resize(**resize_args)
base_pic.composite(overlay_pic, self.px, self.py)
base_pic.save(file=result_pic)
result_pic.seek(0)
while True:
s = result_pic.read(1024)
if not s:
break
pil_parser.feed(s)
pil_result_pic = pil_parser.close()
self.sample.save(self.name, pil_result_pic, False)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.generate_sample()
super(Overlay, self).save(force_insert, force_update, using, update_fields)
But i'm getting AttributeError read here is part on my django debug data:
/usr/local/lib/python2.7/dist-packages/django/core/files/utils.py in <lambda>
"""
encoding = property(lambda self: self.file.encoding)
fileno = property(lambda self: self.file.fileno)
flush = property(lambda self: self.file.flush)
isatty = property(lambda self: self.file.isatty)
newlines = property(lambda self: self.file.newlines)
read = property(lambda self: self.file.read)
readinto = property(lambda self: self.file.readinto)
readline = property(lambda self: self.file.readline)
readlines = property(lambda self: self.file.readlines)
seek = property(lambda self: self.file.seek)
softspace = property(lambda self: self.file.softspace)
tell = property(lambda self: self.file.tell)
▼ Local vars
Variable Value
self <File: None>
/usr/local/lib/python2.7/dist-packages/PIL/Image.py in __getattr__
# numpy array interface support
new = {}
shape, typestr = _conv_type_shape(self)
new['shape'] = shape
new['typestr'] = typestr
new['data'] = self.tobytes()
return new
raise AttributeError(name)
def __getstate__(self):
return [
self.info,
self.mode,
self.size,
▼ Local vars
Variable Value
self <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1080x1618 at 0x7F1429291248>
name 'read'
What's wrong?
Solved!
Such as #Alexey Kuleshevich was saying django FileField need a Fileobjeto, but what was missing is that we must first save the image to a file on disk or in memory, as will guess it's better memory... so here is the final solution. I think it could be improved to not use two step "conversion"
from django.core.files.base import ContentFile
and within the method:
result_pic = io.BytesIO()
pil_parser = Parser()
...
overlay_pic.resize(**resize_args)
base_pic.composite(overlay_pic, self.px, self.py)
base_pic.save(file=result_pic)
result_pic.seek(0)
while True:
s = result_pic.read(1024)
if not s:
break
pil_parser.feed(s)
result_pic = io.BytesIO()
pil_result_pic = pil_parser.close()
pil_result_pic.save(result_pic, format='JPEG')
django_file = ContentFile(result_pic.getvalue())
self.sample.save(self.name, django_file, False)
Thanks to this answer:
How do you convert a PIL Image to a Django File?
Whenever you save a file to ImageField or FileField you need to make sure it is Django's File object. Here the reference to documentation: https://docs.djangoproject.com/en/1.7/ref/models/fields/#filefield-and-fieldfile
from django.core.files import File
and within a method:
def generate_sample(self):
...
pil_result_pic = pil_parser.close()
self.sample.save(self.name, File(pil_result_pic), False)
Otherwise it looks good, although I might have missed something. Try it out and see if it fixes the problem, if not I'll look more into it.
Edit
You actually don't need a parser. I think that should solve it:
from django.core.files import ContentFile
class Overlay(models.Model):
...
def generate_sample(self):
base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg'))
overlay_pic = Image(file=self.source)
result_pic = io.BytesIO()
if self.width or self.height:
resize_args = {}
if self.width:
resize_args['width'] = self.width
if self.height:
resize_args['height'] = self.height
overlay_pic.resize(**resize_args)
base_pic.composite(overlay_pic, self.px, self.py)
base_pic.save(file=result_pic)
content = result_pic.getvalue()
self.sample.save(self.name, ContentFile(content), False)
result_pic.close()
base_pic.close()
overlay_pic.close()
There is one thing that can be a potential problem, it will perform this operation every time Overlay model is saved, even if original images are the same. But if it is saved rarely, it shouldn't be an issue.
Just in case, here is a more elegant (in my opinion) implementation. First of all it requires this app: django-smartfields. How this solution is better:
It updates the sample field only when source field has changed, and only right before saving the model.
if keep_orphans is omitted, old source files will be cleaned up.
The actual code:
import os
from django.conf import settings
from django.db import models
from django.utils import six
from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import WandImageProcessor
from wand.image import Image
class CustomImageProcessor(WandImageProcessor):
def resize(self, image, scale=None, instance=None, **kwargs):
scale = {'width': instance.width, 'height': instance.height}
return super(CustomImageProcessor, self).resize(
image, scale=scale, instance=instance, **kwargs)
def convert(self, image, instance=None, **kwargs):
base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg'))
base_pic.composite(image, instance.px, instance.py)
stream_out = super(CustomImageProcessor, self).convert(
image, instance=instance, **kwargs):
if stream_out is None:
stream_out = six.BytesIO()
base_pic.save(file=stream_out)
return stream_out
class Overlay(models.Model):
RELATIVE_POSITIONS = (...)
SIZE_MODES = (...)
name = models.CharField(max_length=50)
source = fields.ImageField(upload_to='overlays/%Y/%m/%d', dependencies=[
FileDependency(attname='sample', processor=CustomImageProcessor())
], keep_orphans=True)
sample = models.ImageField(upload_to='overlay_samples/%Y/%m/%d', blank=True)
px = models.SmallIntegerField(default=0)
py = models.SmallIntegerField(default=0)
position = models.CharField(max_length=2, choices=RELATIVE_POSITIONS)
width = models.SmallIntegerField(default=0)
height = models.SmallIntegerField(default=0)
size_mode = models.CharField(max_length=1, choices=SIZE_MODES, default='B')
last_edit = models.DateTimeField(auto_now=True)
I have an Openstack-powered, Django-modified application that shows the disk images and snapshots available for a user to launch. The user currently sees both snapshots they created and ones they did not. I would like to separate the current table into two based on whether they are owned by the user or not.
My two table definitions are as follows (note I altered row_actions accordingly):
class UserSnapshotsTable(OldSnapshotsTable):
cloud = tables.Column(get_cloud, verbose_name=_("Cloud"))
class Meta:
name = "usersnapshots"
verbose_name = _("User Snapshots")
table_actions = (DeleteSnapshot,)
row_actions = (LaunchSnapshot, LaunchCluster, EditImage, DeleteSnapshot)
pagination_param = "snapshot_marker"
row_class = UpdateRow
status_columns = ["status"]
class OtherSnapshotsTable(OldSnapshotsTable):
cloud = tables.Column(get_cloud, verbose_name=_("Cloud"))
class Meta:
name = "othersnapshots"
verbose_name = _("Other Snapshots")
table_actions = (DeleteSnapshot,)
row_actions = (LaunchSnapshot, LaunchCluster)
pagination_param = "snapshot_marker"
row_class = UpdateRow
status_columns = ["status"]
I have altered the HTML template to pull the "UserSnapshotsTable" and "OtherSnapshotsTable" tables (I copied the original table and renamed both), but both full tables still generate under the respective headings. There are two functions generating the data:
def get_usersnapshots_data(self):
req = self.request
marker = req.GET.get(UserSnapshotsTable._meta.pagination_param, None)
try:
usersnaps, self._more_snapshots = api.snapshot_list_detailed(req,
marker=marker)
except:
usersnaps = []
exceptions.handle(req, _("Unable to retrieve user-owned snapshots."))
return usersnaps
def get_othersnapshots_data(self):
req = self.request
marker = req.GET.get(OtherSnapshotsTable._meta.pagination_param, None)
try:
othersnaps, self._more_snapshots = api.snapshot_list_detailed(req,
marker=marker)
except:
othersnaps = []
exceptions.handle(req, _("Unable to retrieve non-user-owned snapshots."))
return othersnaps
There are also Edit/Delete options defined for images, and imported for snapshots, that seem to have a key comparison. Here's the "Delete" one (line 7):
class DeleteImage(tables.DeleteAction):
data_type_singular = _("Image")
data_type_plural = _("Images")
def allowed(self, request, image=None):
if image:
return image.owner == request.user.tenant_id
# Return True to allow table-level bulk delete action to appear.
return True
def delete(self, request, obj_id):
api.image_delete(request, obj_id)
How can I separate those tables out? This is my first time asking a question here, so please let me know if I can provide further information. Apologies for the length of it.
As far as I see you are using glanceclient. If that so you can use extra_filters parameter of snapshot_list_detailed() to filter only user images like this:
usersnaps, self._more_snapshots = api.snapshot_list_detailed(
req,
marker = marker,
extra_filters = {"owner": "user_name"}
)
Under cover snapshot_list_detailed uses GET images of Openstack Image Service API.
first_obj = MyFirstModel()
second_obj = ImageModel()
first_obj.name = "newname"
first_obj.phonenr = "9898876"
second_obj.image = "new image"
first_obj.save()
first_obj.create(second_obj) #<------ can i do this?
would this save the second object? is it ever possible to do this?
I think you are getting confused, try this:
class ImageModel(models.Model):
image = models.ImageField()
class MyFirstModel(models.Model):
name = ...
image = models.ForeignKey(Image)
> image_model_instance = ImageModel()
> image_model_instance.save()
> first_model_instance = MyFirstModel(name="foo")
> first_model_instance.image = image_model_instance
> first_model_instance.save()
There is a create() function, but it is used for creating and saving new instances of a model:
first_model_instance = MyFirstModel.objects.create(Name="foo")
so the same as:
first_model_instance = MyFirstModel()
first_model_instance.save()
I'm having trouble getting a model to save (or be put()) correctly. The interesting part is that a model doing a very similar save before it works. Below are the relevant parts of the code. At the two logging points the first correctly returns the email of the user. However, the second one results in the error AttributeError: 'NoneType' object has no attribute 'c_user'. Obviously the setting and un-setting of the variables in this is not the correct way to do things, I've just added these to hunt down the problem to discover that the model isn't being saved. Any suggestions? Thank you much!
class Source(db.Model):
current_user = db.UserProperty()
class SourceMember(db.Model):
c_user = db.UserProperty()
x_position = db.IntegerProperty()
y_position = db.IntegerProperty()
...
user = users.get_current_user()
if user:
source_key = self.request.get('g')
if not source_key:
source_key = user.user_id()
source = Source(key_name = source_key,
current_user = user)
source.put()
else:
source = Source.get_by_key_name(source_key)
source = None
source = Source.get_by_key_name(source_key)
logging.warning(source.current_user)
if source:
sourceMember = SourceMember.get_by_key_name(user.user_id() + source_key)
if not sourceMember:
sourceMember = SourceMember(parent = source.key(),
key_name = user.user_id() + source_key,
c_user = user,
x_position = None,
y_position = None)
sourceMember.put()
sourceMember = None
sourceMember = SourceMember.get_by_key_name(user.user_id() + source_key)
logging.warning(sourceMember.c_user)
When you create the SourceMember you're giving it a parent, but then when you get it, the parent is missing. Source doesn't have a parent, so getting it just from its id works.