Automatically update images - django

I'd like to implement a functionality in an app of mine, but I don't know how to go about it. What I want is this: I have a model class that uses imagekit to save its images, and I'd like to have the users being able to update the images easily for the vehicles without having to edit each respective vehicle record.
How they'll do this is that there will be a folder named originals and it'll contain folders for each vehicle in the format <stock_number>/PUBLIC If a user moves images into the PUBLIC folder for a vehicle, when the script is executed, it'll compare those images with the current ones and update them if those in the PUBLIC folder are newer. If the record has no images, then they will be added. Also, if the images have been deleted from the site_media directory, then their links should be deleted from the database.
How can I go about this in an efficient way? My models are as below:
class Photo(ImageModel):
name = models.CharField(max_length = 100)
original_image = models.ImageField(upload_to = 'photos')
num_views = models.PositiveIntegerField(editable = False, default=0)
position = models.ForeignKey(PhotoPosition)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class IKOptions:
spec_module = 'vehicles.specs'
cache_dir = 'photos'
image_field = 'original_image'
save_count_as = 'num_views'
class Vehicle(models.Model):
objects = VehicleManager()
stock_number = models.CharField(max_length=6, blank=False, unique=True)
vin = models.CharField(max_length=17, blank=False)
....
images = generic.GenericRelation('Photo', blank=True, null=True)
Progress Update
I've tried out the code, and while it works, I'm missing something as I can get the image, but after that, they aren't transferred into the site_media/photos directory...am I suppossed to do this or imagekit will do this automatically? I'm a bit confused.
I'm saving the photos like so:
Photo.objects.create(content_object = vehicle, object_id = vehicle.id,
original_image = file)

My advice is running django script in a crontab job, lets say, 5 in 5 minutes.
The script would dive into the image folders and compare the images with the records.
A simplified example:
# Set up the Django Enviroment
from django.core.management import setup_environ
import settings
setup_environ(settings)
import os
from your_project.your_app.models import *
from datetime import datetime
vehicles_root = '/home/vehicles'
for stock_number in os.listdir(vehicles_root):
cur_path = vehicles_root+'/'+stock_number
if not os.path.isdir(cur_path):
continue # skip non dirs
for file in os.listdir(cur_path):
if not isfile(cur_path+'/'+file):
continue # skip non file
ext = file.split('.')[-1]
if ext.lower() not in ('png','gif','jpg',):
continue # skip non image
last_mod = os.stat(cur_path+'/'+file).st_mtime
v = Vehicle.objects.get(stock_number=stock_number)
if v.last_upd < datetime.fromtimestamp(last_mod):
# do your magic here, move image, etc.
v.last_upd = datetime.now()
v.save()

Related

Change saved filepath in a django celery task

I have a model named Flights
class Flights(models.Model):
field = models.ForeignKey(Field, on_delete=models.CASCADE)
datetime = models.DateTimeField(blank=True, null=True, default=timezone.now())
nir = models.FileField(upload_to = user_directory_path_flights, null=True, blank=True)
red = models.FileField(upload_to = user_directory_path_flights, null=True, blank=True)
rededge = models.FileField(upload_to = user_directory_path_flights, null=True, blank=True)
green = models.FileField(upload_to = user_directory_path_flights, null=True, blank=True)
User uploads some files and through a celery task i get those files and edit them into new ones. After that though they are saved at src folder when i want to save them at src/media/flights/username
How do i do that ? Should i change the Flights model and add a filepath or something? And how so?
celery task :
import sys
import math
from .models import *
import cv2 as cv
import numpy as np
from PIL import Image, ImageOps, ImageChops
import PIL
#shared_task(bind=True)
def get_Flights_Data(self,flight_id):
identifier = Flights.objects.get(pk=flight_id)
redF = identifier.red
nirF = identifier.nir
rededgeF = identifier.rededge
print('Analyzing Flight')
red = Image.open(redF)
nir = Image.open(nirF)
rededge = Image.open(rededgeF)
...............
pil_image=Image.fromarray(ndvi)
img = pil_image
img.save("ndvi_agr.tiff", format="TIFF", save_all=True) #1
Sadly you didn't ask your question correctly so I will try to help you as far as I can.
you can rewrite the previous file saved on the model.
so all you have to do is saving it on the same file path after opening and changing the file.
red= Image.open(redF)
red.save(fp=redF.filepath)
The other option is you can delete the old one and save the new file and give the new file path to your model.
and don't forget to save it.

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

Foreign Keys on Scrapy

im doing an scrap with scrapy and my model on django is:
class Creative(models.Model):
name = models.CharField(max_length=200)
picture = models.CharField(max_length=200, null = True)
class Project(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=500, null = True)
creative = models.ForeignKey(Creative)
class Image(models.Model):
url = models.CharField(max_length=500)
project = models.ForeignKey(Project)
And my scrapy model:
from scrapy.contrib.djangoitem import DjangoItem
from app.models import Project, Creative
class ProjectItems(DjangoItem):
django_model = Project
class CreativeItems(DjangoItem):
django_model = Creative
So when i save:
creative["name"] = hxs.select('//*[#id="owner"]/text()').extract()[0]
picture = hxs.select('//*[#id="owner-icon"]/a/img/#src').extract()
if len(picture)>0:
creative["picture"] = picture[0]
creative.save()
# Extract title and description of the project
project["title"] = hxs.select('//*[#id="project-title"]/text()').extract()[0]
description = hxs.select('//*[#class="project-description"]/text()').extract()
if len(description)>0:
project["description"] = description[0]
project["creative"] = creative
project.save()
I got the error:
Project.creative" must be a "Creative" instance.
So, how can i add a foreing key value on scrapy?
This can be done by assigning the return value of the creative.save() to the value at project['creative'] So for instance in the following example we use the djangoCreativeItem variable to pass this information to the project:
creative["name"] = hxs.select('//*[#id="owner"]/text()').extract()[0]
picture = hxs.select('//*[#id="owner-icon"]/a/img/#src').extract()
if len(picture)>0:
creative["picture"] = picture[0]
djangoCreativeItem = creative.save()
# Extract title and description of the project
project["title"] = hxs.select('//*[#id="project-title"]/text()').extract()[0]
description = hxs.select('//*[#class="project-description"]/text()').extract()
if len(description)>0:
project["description"] = description[0]
project["creative"] = djangoCreativeItem
project.save()
Like it's been done here, put the ID of your creative directly in creative_id, it should work I think:
project["creative_id"] = creative.id
It will specify the foreign key, without bother you with the object missing (because you're in a Scrapy environment where you don't directly touch the model objects...).

Django: changing model's schema (replace CharField with ImageField)

I modify project based on Django framework. I have form to add an item. Item has the cover (an image). Current version of model for this item store cover's url like this:
class Item(models.Model):
title = models.CharField(max_length = 255, db_index = True)
slug = models.CharField(max_length = 80, db_index = True)
categories = models.ManyToManyField(Category)
cover_url = models.CharField(max_length = 255, null = True, default = None)
...
Important notice that some images stored on other servers (different file hosting).
I want to replace CharField with ImageField. But how about existing items? I want to change model's schema and save all previously added images. How I can achieve this goal?
Maybe some reasons for this modification can be helpful. The main reason is to provide to users ability to upload images from their computer (not only insert the urls as it was).
TIA!
If cover_url can have existing source - you must have custom storage, that can handle external sources.
Here is example of custom storage usage for ImageField from django documentation:
from django.db import models
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/media/photos')
class Car(models.Model):
...
photo = models.ImageField(storage=fs)
Let's jump out of it and we will get code like this:
from django.db import models
from django.core.files.storage import FileSystemStorage
def is_url(name):
return 'http' in name
class MyStorage(FileSystemStorage):
#We should override _save method, instead of save.
def _save(self, name, content=None):
if content is None and is_url(name):
return name
super(MyStorage, self)._save(name, content)
fs = MyStorage()
class Item(models.Model):
title = models.CharField(max_length = 255, db_index = True)
slug = models.CharField(max_length = 80, db_index = True)
categories = models.ManyToManyField(Category)
cover_url = models.ImageField(storage=fs)
It has big room for improvements - here is shown only idea.

Save generic images from XML nodes

I'm trying to update my records using XML...so far the first part of the task is done....what I'm wondering is how to get my images onto the saved object (I'm using imagekit for the image handling BTW). My models look like this:
class Photo(ImageModel):
name = models.CharField(max_length=100)
original_image = models.ImageField(upload_to='photos')
num_views = models.PositiveIntegerField(editable=False, default=0)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class IKOptions:
spec_module = 'my_app.specs'
cache_dir = 'photos'
image_field = 'original_image'
save_count_as = 'num_views'
class Room(models.Model):
...
images = generic.GenericRelation('Photo', blank=True, null=True)
...
The XML that I'm using for this is as below:
<room>
<sq_ft>...</sq_ft>
<size>...</size>
<bedrooms>...</bedrooms>
<images>
<image>photos/IMG_3406.JPG</image>
<image>photos/IMG_3416.JPG</image>
<image>photos/IMG_3409.JPG</image>
</images>
</room>
My question is how to get the images for a given room when looping through the XML file and save them against that record.
UPDATE 1
I've tried this bit so far:
if room.getElementsByTagName('image'):
photo = ""
for v in room.getElementsByTagName('images'):
photo = v.childNodes[0].nodeValue
room_photo = Photo.objects.create(content_object = room,
object_id = room.id, original_image = photo)
This does save the photo (somewhat), but then the original_image field is always blank, meaning that I'm doing something wrong in the above piece of code. Any ideas?
Have you taken a look at xml_models? Not sure if it's exactly right for you because I'm not 100% sure of what you're asking. However, it does take care of relationships with models that use XML very smoothly, so may solve your problem incidentally :-)
I think xml_models will work for you. Let me know!
Figured this out as the solution finally:
imagelist = room.getElementsByTagName('image')
if imagelist:
for child in imagelist:
photo = child.childNodes[0].nodeValue
room_photo = Photo.objects.create(content_object = room,
object_id = room.id, original_image = photo)
Thought this might help someone with a similar problem later on.