I'm trying to store an image from a video file in django in the save method of a model. I've based in on this thread
This is my save method for the model
def save(self, *args, **kwargs):
if not self.slug:
orig = self.slug = slugify(unidecode(self.name))
for x in itertools.count(1):
if not Project.objects.filter(slug=self.slug).exists() or (Project.objects.filter(slug=self.slug).exists() and self == Project.objects.get(slug=self.slug)):
break
self.slug = '%s-%d' % (orig, x)
super(Project, self).save(*args, **kwargs)
if not self.image and self.project_type=='video':
vidcap = cv2.VideoCapture(settings.MEDIA_ROOT + self.video.url)
success,image = vidcap.read()
self.image = image
self.image.name = 'video_images/' + self.slug + '.jpg'
new_path = settings.MEDIA_ROOT + self.image.name
cv2.imwrite(new_path, image)
super(Project, self).save(*args, **kwargs)
I've taken a look at success and it always comes out False (I've tried looping while not success and it eventually timesout)
I changed the save to this. It works when the video name is latin characters but not for Greek
if not self.image and self.project_type=='video':
vidcap = cv2.VideoCapture(settings.BASE_DIR + self.video.url)
success,image = vidcap.read()
new_path = settings.MEDIA_ROOT + '/video_images/' + self.slug + '.jpg'
cv2.imwrite(new_path, image)
self.image = new_path
self.image.name = 'video_images/' + self.slug + '.jpg'
Just realised it doesn't work when video has Greek character set
Related
I wanted to post image processing from views to celery, but it shows me a JSON-related problem
"Object of type ImageForm is not JSON serializable"
Here the code from models.py
class ImageModel(models.Model):
title = models.CharField(max_length=20, null=True)
sec_title = models.CharField(max_length=20, null=True)
img = models.ImageField(upload_to='upload_p/', blank=False)
slug = models.SlugField(max_length=250, null=True)
def delete(self, *args, **kwargs):
self.img.delete()
super().delete(*args, **kwargs)
forms.py
class ImageForm(forms.ModelForm):
class Meta:
model = ImageModel
fields = ('title', 'img', 'sec_title')
views.py
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
if form_w.is_valid():
water_mark.delay(form_w)
else:
form = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form': form,
})
tasks.py
#shared_task
def water_mark(form_w):
instance = form_w.save(commit=False)
cd = form_w.cleaned_data['img']
if instance.img:
im = Image.open(instance.img)
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
print('BYTES IO: ', thumb_io)
im.save(thumb_io, im.format, quality=100)
instance.img.save(str(cd), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
return redirect('Luki:gallery')
Of course, all the libraries are imported and the code from the views without celera is executed and it looks like this. This works so that the photo you add in the form gets a watermark, saves it and takes you to the gallery.
views.py
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
if form_w.is_valid():
instance = form_w.save(commit=False)
cd = form_w.cleaned_data['img']
if instance.img:
im = Image.open(instance.img)
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
# draw watermark in the bottom right corner
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
im.save(thumb_io, im.format, quality=100)
instance.img.save(str(cd), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
return redirect('Luki:gallery')
else:
form_w = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form_w': form_w,
})
As per this blog, you can not pass object in celery.
Since Celery is a distributed system, you can't know in which process,
or even on what machine the task will run. So you shouldn't pass
Django model objects as arguments to tasks, its almost always better
to re-fetch the object from the database instead, as there are
possible race conditions involved.
So in your case, save the image in model in view itself and pass pk of the model to celery task, and then fetch those details again from the model.
This is what the code with tasks.py looks like and I still have an error.
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
# files = request.FILES.getlist('img')
if form_w.is_valid():
wm = form_w.save()
water_mark.delay(wm.pk)
return redirect('Luki:gallery')
else:
form_w = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form_w': form_w,
})
And tasks.py
def water_mark(wm):
instance = ImageModel.objects.get(id=wm)
# im = Image.open(instance.img)
# print('im before saving: ', im)
# Writes the image correctly
# im.save('Luki/media/upload_p/zzzta.jpg')
if instance.img:
# upload_p/img-8945.jpg
print('img before opening: ', instance.img)
# returns upload_p/img-8945.jpg
im = Image.open(instance.img)
print('img after: ', im)
# returns <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1080 at 0x5AD75F0>
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
# draw watermark in the bottom right corner
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
print('BYTES IO: ', thumb_io)
im.save(thumb_io, im.format, quality=100)
# FieldFile.save(name, content, save=True)
instance.img.save(str('zzjca.jpg'), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
And it returns an error
File "f:\python\portfoliolk\venv\lib\site-packages\eventlet\greenio\base.py", line 100, in set_nonblocking raise NotImplementedError("set_nonblocking() on a file object " NotImplementedError: set_nonblocking() on a file object with no setblocking() method (Windows pipes don't support non-blocking I/O)
I don't know if it's not windows fault...
I'm trying to paginate with different things to maximize elements, on the first page (5) and on all the others (maximum 6).
I do like in this answer
class MyPaginator(Paginator):
def __init__(self, **kw):
self.deltafirst = kw.pop('deltafirst', 0)
Paginator.__init__(self, **kw)
def page(self, number):
number = self.validate_number(number)
if number == 1:
bottom = 0
top = self.per_page - self.deltafirst
else:
bottom = (number - 1) * self.per_page - self.deltafirst
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
class NewsView(ListView):
model = News
template_name="mysite/news.html"
paginate_by = 5
def get_context_data(self, **kwargs):
context = super(NewsView, self).get_context_data(**kwargs)
all_news = News.objects.all().order_by("-date_news")
paginator = MyPaginator(all_news, self.paginate_by,deltafirst=1)
page = self.request.GET.get('page')
try:
file_exams = paginator.page(page)
except PageNotAnInteger:
file_exams = paginator.page(1)
except EmptyPage:
file_exams = paginator.page(paginator.num_pages)
context['all_news'] = file_exams
return context
But I get an error and I can't figure out what I'm doing wrong. The error itself
paginator = MyPaginator(all_news, self.paginate_by,deltafirst=1)
TypeError: __init__() takes 1 positional argument but 3 were given
Firstly change your __init__ method in MyPaginator to fix the error:
from math import ceil
class MyPaginator(Paginator):
def __init__(self, *args, **kw):
self.deltafirst = kw.pop('deltafirst', 0)
super().__init__(*args, **kw)
def page(self, number):
number = self.validate_number(number)
if number == 1:
bottom = 0
top = self.per_page - self.deltafirst
else:
bottom = (number - 1) * self.per_page - self.deltafirst
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
#property
def num_pages(self):
if self.count == 0 and not self.allow_empty_first_page:
return 0
count = max(self.count - self.per_page + self.deltafirst, 0)
hits = max(0, count - self.orphans)
return 1 + ceil(hits / self.per_page)
Next to make your view simpler instead of overriding get_context_data override get_paginator and also set deltafirst as a class attribute:
class NewsView(ListView):
model = News
template_name="mysite/news.html"
paginate_by = 5
paginator_class = MyPaginator
deltafirst = 1
def get_paginator(self, *args, **kwargs):
if self.deltafirst:
kwargs['deltafirst'] = self.deltafirst
return super().get_paginator(*args, **kwargs)
I'm trying this code, but I'm doing something wrong.
class Photo(TimestampedModel):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
image = models.ImageField(upload_to="photos")
image_600 = ImageSpecField(source='image',
processors=[ResizeToFill(600, 600)],
format='JPEG',
options={'quality': 70})
def save(self, *args, **kwargs):
if not self.id:
self.image = self.compressImage(self.image)
super(Photo, self).save(*args, **kwargs)
def compressImage(self, image):
imageTemporary = Image.open(image)
format = imageTemporary.format
outputIoStream = BytesIO()
imageTemporary.save(outputIoStream, format=format, quality=70)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream, 'ImageField', image.name,
format, None, None)
return image
def resizeImage(self, image):
basewidth = 600
img = Image.open(image)
format = img.format
wpercent = (basewidth/float(img.size[0]))
hsize = int((float(img.size[1])*float(wpercent)))
img = img.resize((basewidth,hsize), Image.ANTIALIAS)
outputIoStream = BytesIO()
img.save(outputIoStream, format=format, quality=70)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream, 'ImageField', image.name,
format, None, None)
return image
The idea is to create a second image (image_card), with a new size.
In the end, I get an image for image_card but not for image.
The methods compressImage() and resizeImage() works correctly, but I suspect that resizeImage is getting the same image instance.
If I comment the line self.image_card = self.resizeImage(self.image) , image works again.
I'm trying to make a form that can be used to make a new instance of "LearningObjects" as well as edit existing instances. It seems to work fine except that when I'm editing an existing instance I lose the filefield. Since it is a required field it asks me to upload a new file and obviously I don't always want to do that.
Form.py
class LearningObjectuploadform(forms.ModelForm, edit):
level = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple,queryset=None,required=False)
agebracket =forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple,queryset=None,required=False)
pathway = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple,queryset=None,required=False)
class Meta:
model = LearningObject
fields =['title','archivefile','description','tags','pathway','level','subject','agebracket']
def __init__(self, *args, **kwargs):
super(LearningObjectuploadform, self).__init__(*args, **kwargs)
self.fields['level'].queryset = AssoeLevel.objects.all()
self.fields['pathway'].queryset = AssoePathway.objects.all()
self.fields['agebracket'].queryset = AgeBracket.objects.all()
View.py
def createLearningobject(request,learningobject_pk=False):
if request.method == 'GET':
if learningobject_pk:
print learningobject_pk
instance = LearningObject.objects.get( pk=learningobject_pk)
print instance
form = LearningObjectuploadform(request.POST or None, request.FILES or None, instance=instance)
else:
form = LearningObjectuploadform()
else:
form = LearningObjectuploadform(request.POST, request.FILES)
if form.is_valid():
learningobject = request.FILES['archivefile']
title = form.cleaned_data['title']
description = form.cleaned_data['description']
tags = form.cleaned_data['tags']
levels = form.cleaned_data['level']
pathways = form.cleaned_data['pathway']
agebrackets = form.cleaned_data['agebracket']
post = LearningObject.objects.create(archivefile=learningobject,title=title, description=description)
for tag in tags:
post.tags.add(tag)
for level in levels:
post.level.add(level.pk)
for pathway in pathways:
post.pathway.add(pathway.pk)
for agebracket in agebrackets:
post.agebracket.add(agebracket.pk)
post.save()
return HttpResponseRedirect(reverse('index', ))
else:
print "form not valid"
return render(request, 'mediamanager/edit_learningobject.html', {'form': form,})
Models.py
class DefaultResource(models.Model):
#
# This class is the parent class for all resources in the media manager
#
title = models.CharField(max_length=100)
created_date = models.DateTimeField(auto_now_add=True, auto_now=False)
edited_date = models.DateTimeField(auto_now_add=False,auto_now=True)
level = models.ManyToManyField(AssoeLevel)
agebracket= models.ManyToManyField(AgeBracket)
pathway= models.ManyToManyField(AssoePathway)
tags = TaggableManager()
slug = models.SlugField(max_length=100,editable=False,blank=True)
updownvotes = RatingField(can_change_vote=True)
views = models.DecimalField(max_digits=20,decimal_places=2,default=0,blank=True)
score = models.DecimalField(max_digits=20,decimal_places=4,default=0,blank=True)
icon = models.CharField(max_length=254,editable=False,blank=True)
subject = models.ManyToManyField(AssoeSubjects)
#def return_tags(self):
# taglist = self.tags.names()
# return taglist
def calculate_score(self):
score = float(self.updownvotes.likes) - float(self.updownvotes.dislikes)
score = score + (float(self.views)**(float(1)/float(2)))
self.score = score
rounded_score = int(round(self.score))
if rounded_score < -1:
return -1
else:
return rounded_score
def __unicode__ (self):
return self.title
def save(self, *args, **kwargs):
self.calculate_score()
if not self.id:
self.slug = slugify(self.title)
super(DefaultResource, self).save(*args, **kwargs)
class LearningObject(DefaultResource):
archivefile = models.FileField(upload_to='static/learningobject/archivefiles/%Y/%m/%d')
indexpath = models.CharField(max_length=254,editable=False,blank=True)
description = models.TextField(blank=True)
def unpackarchive(self):
archive = self.archivefile
filename = os.path.basename(str(archive))
folder = str(filename).split(".")[0]
print folder
index_found = "False"
with zipfile.ZipFile(archive,"r") as z:
for each in z.namelist():
if each == "index.html" or each == "index.htm":
index_found = "True"
else:
pass
if not index_found:
print "zip file does not contain a valid index.html file"
else:
path = os.path.join("static","learningobject","unpackedarchives",folder)
z.extractall(path)
self.findindex(path)
def findindex(self,path):
print path
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, 'index.ht*'):
print filename
self.indexpath = os.path.join(root, filename)
print self.indexpath
def save(self, *args, **kwargs):
self.icon = "/static/images/icons/box.png"
self.unpackarchive()
super(LearningObject, self).save(*args, **kwargs)
I have faced similar problems with file uploads in django forms. This is what I hope should help you out(provided you are willing to alter the required attribute of the archivefile field.)
if request.method == 'GET':
if learningobject_pk:
print learningobject_pk
instance = LearningObject.objects.get( pk=learningobject_pk)
print instance
form = LearningObjectuploadform(request.POST or None, request.FILES or None, instance=instance)
form.base_fields['archivefile'].required = False
else:
form = LearningObjectuploadform()
I have problem with getting additional resized images from croped image. I am using django-cropper 0.1.
Originally model for Cropped Image have part of code like this:
class Cropped(models.Model):
def __unicode__(self):
return u'%s-%sx%s' % (self.original, self.w, self.h)
def upload_image(self, filename):
return '%s/crop-%s' % (settings.ROOT, filename)
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
super(Cropped, self).save(*args, **kwargs)
But i want to have additional resized images from cropped image so I change code a little and now it looks like that:
class Cropped(models.Model):
def __unicode__(self):
return u'%s-%sx%s' % (self.original, self.w, self.h)
def _get_average_path(self):
return _add_average(self.path)
average_path = property(_get_average_path)
def _get_average_url(self):
return _add_average(self.url)
average_url = property(_get_average_url)
def _get_large_path(self):
return _add_large(self.path)
large_path = property(_get_large_path)
def _get_large_url(self):
return _add_large(self.url)
large_url = property(_get_large_url)
def upload_image(self, filename):
return '%s/crop-%s' % (settings.ROOT, filename)
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
img = Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
super(Cropped, self).save(*args, **kwargs)
img = Image.open(self.image.path)
img = img.resize((180, 180), Image.ANTIALIAS)
img.save(self.large_path, 'JPEG')
img = img.resize((104, 104), Image.ANTIALIAS)
img.save(self.average_path, 'JPEG')
But it still doing the basic job. Can someone help me and give any suggestion of what should I fix in this code?
I answer my own question because i found solution and maybe it someone. Maybe it is now the best solution, but it works. First it is needed to add additional field for additional, resized image:
average = models.ImageField(
blank=True, null=True,
verbose_name = _('Image'),
upload_to = upload_image,
editable = False,
)
Then it is needed to chnge save() method to something like this:
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
splited_target = target.split("/")
avepath = "/average-".join(splited_target)
self.average = avepath
Image.open(self.image).resize((104, 104), Image.ANTIALIAS).save(django_settings.MEDIA_ROOT + avepath, 'JPEG')
super(Cropped, self).save(*args, **kwargs)
And that is all.