Django Signals - How to save the instance - django

I am using Django Rest Framework to upload videos. Now I wanted to add thumbnails to my video files. Whenever a Video object is saved, I wanted to create a thumbnail and set its thumbnail field.
My Video model looks like this:
class Video(models.Model):
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=100, blank=True)
video = models.FileField(upload_to='Videos/',blank=True)
thumbnail = models.ImageField(upload_to = 'Images/', blank = True)
My signal handler looks like this:
from moviepy.video.io.VideoFileClip import VideoFileClip
from posts.models import Video
from django.db.models.signals import post_save
from django.dispatch import receiver
from settingsFolderOfProject.settings import MEDIA_ROOT
import os
# when save() of Video is done, create_thumbnail_from_video() is called
#receiver(post_save, sender=Video)
def create_thumbnail_from_video(sender, instance, created, **kwargs):
if created:
# create the clip using moviepy's VideoFileClip class
clip = VideoFileClip(os.path.join(MEDIA_ROOT, instance.video.name))
# create the frame at 1st second and set it to instance's thumbnail field
instance.thumbnail = clip.save_frame(os.path.join(MEDIA_ROOT, 'Images/thumbnail.jpg'),t='00:00:01')
# save the instance
instance.save() # <--- I think this op does not work properly
What I have to change?
The thumbnail file gets created in the folder as expected but Django does not set the thumbnail field of my Video model instance. The thumbnail field of the created Video instance is still set to null.

clip.save_frame() doesn't return anything.
Instead do
path = os.path.join(MEDIA_ROOT, 'Images/thumbnail.jpg')
clip.save_frame(path,t='00:00:01')
instance.thumbnail = path
instance.save()
Note: Haven't tested yet. Comment if problem still persists. Thnx...

Related

Automatically save list of images in ImageField after upload - django 3

Working with Django 3, I have the following model:
class Case(models.Model):
slug = ShortUUIDField(max_length=3)
title = models.CharField(max_length=50)
#....
ct = models.FileField(upload_to='files/')
class ModelImages(models.Model):
case = models.ForeignKey(Case, on_delete=models.CASCADE,
related_name="slices_images")
img = models.ImageField(upload_to='images/', editable=False)
#....
position = models.PositiveSmallIntegerField(editable=False)
Once 'ct' (a 3D image) is uploaded (and this works perfectly), I want to extract the slices of the 'ct' and save them as images in 'ModelImages' class.
I extract the images with a function called 'get_slices(ct)'
The images are hundreds for each 'ct'
This should be fully automatic.
How can I do it?
Here is my suggestion for you. Use a post save signal that will 'automatically' process the 3D image of a Case object after it is saved.
from django.db.models.signals import post_save
class Case(models.Model):
slug = ShortUUIDField(max_length=3)
title = models.CharField(max_length=50)
#....
ct = models.FileField(upload_to='files/')
class ModelImages(models.Model):
case = models.ForeignKey(Case, on_delete=models.CASCADE,
related_name="slices_images")
img = models.ImageField(upload_to='images/', editable=False)
#....
position = models.PositiveSmallIntegerField(editable=False)
# This will be run anytime AFTER a Case object is saved.
def process_3d_image(sender, instance, created, **kwargs):
# this is run only on first save (creation)
if created:
# if get_slices is creating and saving ModelImages objects you can pass
# the instance (which is the Case object) for the foreign key as well.
get_slices(instance.ct)
post_save.connect(process_3d_image, sender=Case)
Beware! This will be a synchronous task meaning django will wait for this processing to be done before returning a response. Seeing as there are hundreds of images this could take a while..
P.S..
My second suggestion would be to avoid this by using an asynchronous task manager such as Celery.
If you manage to setup Celery you could create an asynchronous task for saving the images from ct that would be done in the background without blocking your django app.
tasks.py
from <module> import get_slices
#app.task()
def process_3d_image_async(case_ct):
get_slices(case_ct)
modified post_save function if using Celery
from .tasks import process_3d_image_async
# This will be run whenever a Case object is saved.
def process_3d_image(sender, instance, created, **kwargs):
# this is run only on first save (creation)
if created:
process_3d_image_async.delay(case_ct=instance.ct)
And last thing to note is if you do it asynchronously you'll want to know whether a Case object's file has finished being processed so my last suggestion would be to add a BooleanField column to your Case model with default value False that you then set to true in your async task after slicing.
However using Celery is only a suggestion!

django.db.models ImageField save Image as Base64

I'm actually new to Django. Let's say I have this model as a class
class UserAccount(AbstractBaseUser, PermissionsMixin):
...
profile_photo = models.ImageField(
upload_to='photos/profile-picture', blank=True)
objects = UserAccountManager()
def __str__(self):
return self.email
I want to save the profile_photo attribute as a base64 file (instead of file path). How do I do that?
Context : In my client side (front-end), I can't render the image by getting its file path. So I want to save the image as base64 string in the DB instead of actual image path so that it will be easier to render in the front end
ImageField has "url" property. It's will combine with MEDIA_URL in the project's setting.

Django model field which refers to ImageField of another model

I have two models:
class A(models.Model):
title = models.CharField(max_length=100)
a_bg_img = models.ImageField(upload_to='./bg/')
class A_B(models.Model):
title = models.CharField(max_length=100)
b_bg_img = # this should just refer to A's a_bg_img field
b_bg_img should just refer to a_bg_img Field so that I dont save one Image twice. can I just say:
b_bg_img = models.TextField()
and then save there only the link to Image in a_bg_img ?
If you have an image associated with a class A object, then you can easily retrieve the path of that image or url of that image using .path( or get_path()) or .url.
For example:
a = A.objects.get(id=1)
print(a.a_bg_img.path)
'/path/to/image/in/your/local/drive/image.jpg'
print(a.a_bg_img.get_path())
'/path/to/image/in/your/local/drive/image.jpg'
print(a.a_bg_img.url)
'/url/to/your/image'
You can use the django signals post_save to perform actions after the models save()
How ?
create a signals.py file in your app
import it in __init__.py of your main app folder
Inside the signals.py app create function like below:
from django.dispatch import receiver
from django.db.models.signals import post_save
#receiver(post_save, sender=A)
def my_action(sender, instance, **kwargs):
# Your function magic

Changing name of image uploaded

I'm a noob django developer, I can upload images but name of the images have to be changed to image ID or whatever I want.
In addition to this, when I delete entries, images are still resting in the media file but I don't want this.
What I have to add ?
Here is my models.py
from django.db import models
class Flower(models.Model):
name = models.CharField(max_length = 30)
price = models.IntegerField()
image = models.ImageField(upload_to = 'static/media')
def __unicode__(self):
return self.name
To customize the path and filename of where the files are stored, you'll need to define a method for upload_to. Here's an example:
def my_upload_to(instance, filename):
# "instance" is an instance of Flower
# return a path here
return 'my/path/to/storage/' + filename
class Flower(models.Model):
image = models.ImageField(upload_to=my_upload_to)
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
To delete the underlying file, you'll need to make a call manually:
flower = Flower.objects.get(id=1)
flower.image.delete()
You can choose to override your model's delete() method, or use signals pre_delete or post_delete to delete the associated files automatically.
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield-and-fieldfile

Getting IOError when using pre_save signal to store a thumbnail

I have a model that has an option photo field. When a photo is added, I want a thumbnail to be automatically created and stored. However, when I do this with a pre_save signal, I keep getting an IOError, and if I try to do it with a post_save signal I can't save the thumbnails path to my model without creating and infinite post_save loop.
Here's the code
# using PIL
from PIL import Image
import os
...
# my model
class Course(models.Model):
...
photo = models.ImageField(upload_to='course_images/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='course_images/thumbnails/', blank=True, null=True, editable=False)
...
# my pre_save signal
def resize_image(sender, instance, *args, **kwargs):
'''Creates a 125x125 thumbnail for the photo in instance.photo'''
if instance.photo:
image = Image.open(instance.photo.path)
image.thumbnail((125, 125), Image.ANTIALIAS)
(head, tail) = os.path.split(instance.photo.path)
(a, b) = os.path.split(instance.photo.name)
image.save(head + '/thumbnails/' + tail)
instance.thumbnail = a + '/thumbnails/' + b
models.signals.pre_save.connect(resize_image, sender=Course)
I figured it out. The problem I was having was trying to save the thumbnail field, and I was trying to do that within a signal. So to fix that I save the thumbnail field in the models save() function instead, and leave the signal to create the thumbnail.
Just took me awhile to figure out :/