Is there a faster way of uploading multiple files in Django? - python

I have a django project where the client can upload multiple files at once. My problem is that for each uploaded file I'm creating a model object - one at a time. Is there a way to do this with bulk create or some other method that is faster.
Views.py:
images = request.FILES.getlist('images')
xmls = request.FILES.getlist('xmls')
jsons = request.FILES.getlist('jsons')
for image in images:
img_name = (str(image).split('.')[0])
dp_name = dataset_name+'-'+img_name
if [xml for xml in xmls if img_name+'.' in str(xml)]:
xml = [xml for xml in xmls if img_name+'.' in str(xml)][0]
else:
xml = None
if [json for json in jsons if img_name+'.' in str(json)]:
json = [json for json in jsons if img_name+'.' in str(json)][0]
else:
json = None
dataset.create_datapoint(image, xml, json, username, dp_name)
Models.py:
def create_datapoint(self, image, xml, json, username, dp_name):
datapoint = Datapoint.objects.create(
xml = xml,
json = json,
name = dp_name,
uploaded_by = username,
img = image,
belongs_to = self,
)
self.num_datapoints += 1
datapoint.parse_xml()
self.datapoints.add(datapoint)
self.save()
return

#mnislam01 is right but there is a small mistake in the code provided.
Here it is fixed:
data_point_create_list = []
# First create a list of objects.
for image in images:
dp = Datapoint(xml = xml,
json = json,
name = dp_name,
uploaded_by = username,
img = image,
belongs_to = self)
data_point_create_list.append(dp)
# Then bulk create all the objects.
if data_point_create_list:
Datapoint.objects.bulk_create(data_point_create_list)
Just needed to assign the newly made datapoint before appending.

You can use .bulk_create() method. For example.
data_point_create_list = []
# First create a list of objects.
for image in images:
dp = Datapoint(xml = xml,
json = json,
name = dp_name,
uploaded_by = username,
img = image,
belongs_to = self)
data_point_create_list.append(dp)
# Then bulk create all the objects.
if data_point_create_list:
Datapoint.objects.bulk_create(data_point_create_list)

Related

Django Object gets created twice

So I have a big app and was able to find out where the "error" is located (That's why I don't need to upload more of my code)
image_model = Image_model(
user = user,
lan = lan,
lon = lon,
width = width,
height = height,
image_name = image_name,
image_name_preview = image_name_preview,
image = original_image,
preview_image = preview_image,
whatisseen = whatisseen,
timestamp_create = timestamp_create
)
image_model.save()
RoomChatMessage.objects.create(
room = room,
user = user,
content = message,
image = image_modal,
)
payload = {"Success": "Message successfully added"}
So when I call the function around the two objects the image object is created and after that the RoomChatMessage object. But now I have a problem:
When I commend the image_modal out (the full object) and only create the RoomChatMessage (without the image) it works fine. But if I call the function like this I get one Image Object and two Message Objects - but why?

Scraping all the images from a specific part of a webpage using BeautifulSoup

Object 'gallery' is what I got - how would I be able to just select the image urls without going a long way around.
Currently, I am doing the following
from bs4 import BeautifulSoup
from PIL import Image
import requests
gallery = soup.findAll(class_='gallery')
img_0 = gallery[0].find('img')
img_1 = gallery[1].find('img')
...
img_x = gallery[x].find('img')
img_url_0 = img_0['src']
img_url_1 = img_1['src']
...
img_url_x = img_x['src']
gallery_img_0 = Image.open(requests.get(img_url_0, stream = True).raw)
gallery_img_1 = Image.open(requests.get(img_url_1, stream = True).raw)
...
gallery_img_x = Image.open(requests.get(img_url_x, stream = True).raw)
where x is the length of the gallery iterable.
Perhaps a loop? :s
Thanks, CN
You can use nested loop to load all image and store them to a list. For example:
galleries = soup.findAll(class_='gallery')
all_images = []
for gallery in galleries:
for img in gallery.findAll('img'):
gallery_img = Image.open(requests.get(img['src'], stream = True).raw)
all_images.append(gallery_img)
# here, `all_images` contains all images
# ...

How to annotate MULTIPLE images from a single call using Google's vision API? Python

I recently started using Google's vision API. I am trying to annotate a batch of images and therefore issued the 'batch image annotation offline' guide from their documentation.
However, it is not clear to me how I can annotate MULTIPLE images from one API call. So let's say I have stored 10 images in my google cloud bucket. How can I annotate all these images at once and store them in one JSON file? Right now, I wrote a program that calls their example function and it works, but to put it simple, why can't I say: 'Look in this folder and annotate all images in it.'?
Thanks in advance.
from batch_image_labeling import sample_async_batch_annotate_images
counter = 0
for file in os.listdir('my_directory'):
filename = file
sample_async_batch_annotate_images('gs://my_bucket/{}'.format(filename), 'gs://my_bucket/{}'.format(counter))
counter += 1
from google.cloud import vision_v1
from google.cloud.vision_v1 import enums
import six
def sample_async_batch_annotate_images(input_image_uri, output_uri):
"""Perform async batch image annotation"""
client = vision_v1.ImageAnnotatorClient()
if isinstance(input_image_uri, six.binary_type):
input_image_uri = input_image_uri.decode('utf-8')
if isinstance(output_uri, six.binary_type):
output_uri = output_uri.decode('utf-8')
source = {'image_uri': input_image_uri}
image = {'source': source}
type_ = enums.Feature.Type.LABEL_DETECTION
features_element = {'type': type_}
type_2 = enums.Feature.Type.IMAGE_PROPERTIES
features_element_2 = {'type': type_2}
features = [features_element, features_element_2]
requests_element = {'image': image, 'features': features}
requests = [requests_element]
gcs_destination = {'uri': output_uri}
# The max number of responses to output in each JSON file
batch_size = 2
output_config = {'gcs_destination': gcs_destination, 'batch_size': batch_size}
operation = client.async_batch_annotate_images(requests, output_config)
print('Waiting for operation to complete...')
response = operation.result()
# The output is written to GCS with the provided output_uri as prefix
gcs_output_uri = response.output_config.gcs_destination.uri
print('Output written to GCS with prefix: {}'.format(gcs_output_uri))
It's somewhat unclear from that example, but your call to async_batch_annotate_images takes a requests parameter which is a list of multiple requests. So you can do something like this:
rom google.cloud import vision_v1
from google.cloud.vision_v1 import enums
import six
def generate_request(input_image_uri):
if isinstance(input_image_uri, six.binary_type):
input_image_uri = input_image_uri.decode('utf-8')
if isinstance(output_uri, six.binary_type):
output_uri = output_uri.decode('utf-8')
source = {'image_uri': input_image_uri}
image = {'source': source}
type_ = enums.Feature.Type.LABEL_DETECTION
features_element = {'type': type_}
type_2 = enums.Feature.Type.IMAGE_PROPERTIES
features_element_2 = {'type': type_2}
features = [features_element, features_element_2]
requests_element = {'image': image, 'features': features}
return requests_element
def sample_async_batch_annotate_images(input_uri, output_uri):
"""Perform async batch image annotation"""
client = vision_v1.ImageAnnotatorClient()
requests = [
generate_request(input_uri.format(filename))
for filename in os.listdir('my_directory')
]
gcs_destination = {'uri': output_uri}
# The max number of responses to output in each JSON file
batch_size = 1
output_config = {'gcs_destination': gcs_destination, 'batch_size': batch_size}
operation = client.async_batch_annotate_images(requests, output_config)
print('Waiting for operation to complete...')
response = operation.result()
# The output is written to GCS with the provided output_uri as prefix
gcs_output_uri = response.output_config.gcs_destination.uri
print('Output written to GCS with prefix: {}'.format(gcs_output_uri))
sample_async_batch_annotate_images('gs://my_bucket/{}', 'gs://my_bucket/results')
This can annotate up to 2,000 images in a single request. The only downside is that you can only specify a single output_uri as a destination, so you won't be able to use counter to put each result in a separate file, but you can set batch_size = 1 to ensure each response is written separately if this is what you want.

Pass id parameter into imported class in Django

In Django I have a function based view responsible of printing the details (actually only the name) of all the registered users on a pdf file.
def test_pdf(request, id):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="My Users.pdf"'
buffer = io.BytesIO()
report = MyPrint(buffer, 'Letter', id)
pdf = report.print_users()
response.write(pdf)
return response
This function works because I imported in the views.py file a class I built in another file, responsible of drawing the pdf, MyPrint:
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER
from django.contrib.auth.models import User
class MyPrint:
def __init__(self, buffer, pagesize):
self.buffer = buffer
if pagesize == 'A4':
self.pagesize = A4
elif pagesize == 'Letter':
self.pagesize = letter
self.width, self.height = self.pagesize
def print_users(self):
buffer = self.buffer
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
# Our container for 'Flowable' objects
elements = []
# A large collection of style sheets pre-made for us
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='centered', alignment=TA_CENTER))
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
users = User.objects.all()
elements.append(Paragraph('My User Names', styles['Heading1']))
for i, user in enumerate(users):
elements.append(Paragraph(user.get_full_name(), styles['Normal']))
doc.build(elements)
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
Now, How can I make the function and the class specific to a user if I pass in the relative pk into the function? Apart from updating the urlpattern, should I pass the id into the class and / or into the function?
If you want to have the existing function work with one or more users, and continue to work if you don't pass in an id, I think the simplest way of changing it would be as follows:
def print_users(self, id=None):
buffer = self.buffer
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
# Our container for 'Flowable' objects
elements = []
# A large collection of style sheets pre-made for us
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='centered', alignment=TA_CENTER))
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
users = User.objects.all()
if id:
users = users.filter(id__in=id)
elements.append(Paragraph('My User Names', styles['Heading1']))
for i, user in enumerate(users):
elements.append(Paragraph(user.get_full_name(), styles['Normal']))
doc.build(elements)
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
Then change how you call it to:
report = MyPrint(buffer, 'Letter')
pdf = report.print_users(id)
or, if you want to print all users, just call it as:
report = MyPrint(buffer, 'Letter')
pdf = report.print_users()

How do you convert a PIL `Image` to a Django `File`?

I'm trying to convert an UploadedFile to a PIL Image object to thumbnail it, and then convert the PIL Image object that my thumbnail function returns back into a File object. How can I do this?
The way to do this without having to write back to the filesystem, and then bring the file back into memory via an open call, is to make use of StringIO and Django InMemoryUploadedFile. Here is a quick sample on how you might do this. This assumes that you already have a thumbnailed image named 'thumb':
import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
# Create a file-like object to write thumb data (thumb data previously created
# using PIL, and stored in variable 'thumb')
thumb_io = StringIO.StringIO()
thumb.save(thumb_io, format='JPEG')
# Create a new Django file-like object to be used in models as ImageField using
# InMemoryUploadedFile. If you look at the source in Django, a
# SimpleUploadedFile is essentially instantiated similarly to what is shown here
thumb_file = InMemoryUploadedFile(thumb_io, None, 'foo.jpg', 'image/jpeg',
thumb_io.len, None)
# Once you have a Django file-like object, you may assign it to your ImageField
# and save.
...
Let me know if you need more clarification. I have this working in my project right now, uploading to S3 using django-storages. This took me the better part of a day to properly find the solution here.
I've had to do this in a few steps, imagejpeg() in php requires a similar process. Not to say theres no way to keep things in memory, but this method gives you a file reference to both the original image and thumb (usually a good idea in case you have to go back and change your thumb size).
save the file
open it from filesystem with PIL,
save to a temp directory with PIL,
then open as a Django file for this to work.
Model:
class YourModel(Model):
img = models.ImageField(upload_to='photos')
thumb = models.ImageField(upload_to='thumbs')
Usage:
#in upload code
uploaded = request.FILES['photo']
from django.core.files.base import ContentFile
file_content = ContentFile(uploaded.read())
new_file = YourModel()
#1 - get it into the DB and file system so we know the real path
new_file.img.save(str(new_file.id) + '.jpg', file_content)
new_file.save()
from PIL import Image
import os.path
#2, open it from the location django stuck it
thumb = Image.open(new_file.img.path)
thumb.thumbnail(100, 100)
#make tmp filename based on id of the model
filename = str(new_file.id)
#3. save the thumbnail to a temp dir
temp_image = open(os.path.join('/tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')
#4. read the temp file back into a File
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)
new_file.thumb.save(str(new_file.id) + '.jpg', thumb_file)
This is actual working example for python 3.5 and django 1.10
in views.py:
from io import BytesIO
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import InMemoryUploadedFile
def pill(image_io):
im = Image.open(image_io)
ltrb_border = (0, 0, 0, 10)
im_with_border = ImageOps.expand(im, border=ltrb_border, fill='white')
buffer = BytesIO()
im_with_border.save(fp=buffer, format='JPEG')
buff_val = buffer.getvalue()
return ContentFile(buff_val)
def save_img(request)
if request.POST:
new_record = AddNewRecordForm(request.POST, request.FILES)
pillow_image = pill(request.FILES['image'])
image_file = InMemoryUploadedFile(pillow_image, None, 'foo.jpg', 'image/jpeg', pillow_image.tell, None)
request.FILES['image'] = image_file # really need rewrite img in POST for success form validation
new_record.image = request.FILES['image']
new_record.save()
return redirect(...)
Putting together comments and updates for Python 3+
from io import BytesIO
from django.core.files.base import ContentFile
import requests
# Read a file in
r = request.get(image_url)
image = r.content
scr = Image.open(BytesIO(image))
# Perform an image operation like resize:
width, height = scr.size
new_width = 320
new_height = int(new_width * height / width)
img = scr.resize((new_width, new_height))
# Get the Django file object
thumb_io = BytesIO()
img.save(thumb_io, format='JPEG')
photo_smaller = ContentFile(thumb_io.getvalue())
To complete for those who, like me, want to couple it with Django's FileSystemStorage:
(What I do here is upload an image, resize it to 2 dimensions and save both files.
utils.py
def resize_and_save(file):
size = 1024, 1024
thumbnail_size = 300, 300
uploaded_file_url = getURLforFile(file, size, MEDIA_ROOT)
uploaded_thumbnail_url = getURLforFile(file, thumbnail_size, THUMBNAIL_ROOT)
return [uploaded_file_url, uploaded_thumbnail_url]
def getURLforFile(file, size, location):
img = Image.open(file)
img.thumbnail(size, Image.ANTIALIAS)
thumb_io = BytesIO()
img.save(thumb_io, format='JPEG')
thumb_file = InMemoryUploadedFile(thumb_io, None, file.name, 'image/jpeg', thumb_io.tell, None)
fs = FileSystemStorage(location=location)
filename = fs.save(file.name, thumb_file)
return fs.url(filename)
In views.py
if request.FILES:
fl, thumbnail = resize_and_save(request.FILES['avatar'])
#delete old profile picture before saving new one
try:
os.remove(BASE_DIR + user.userprofile.avatarURL)
except Exception as e:
pass
user.userprofile.avatarURL = fl
user.userprofile.thumbnailURL = thumbnail
user.userprofile.save()
Here is an app that can do that: django-smartfields
from django.db import models
from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor
class ImageModel(models.Model):
image = fields.ImageField(dependencies=[
FileDependency(processor=ImageProcessor(
scale={'max_width': 150, 'max_height': 150}))
])
Make sure to pass keep_orphans=True to the field, if you want to keep old files, otherwise they are cleaned up upon replacement.
For those using django-storages/-redux to store the image file on S3, here's the path I took (the example below creates a thumbnail of an existing image):
from PIL import Image
import StringIO
from django.core.files.storage import default_storage
try:
# example 1: use a local file
image = Image.open('my_image.jpg')
# example 2: use a model's ImageField
image = Image.open(my_model_instance.image_field)
image.thumbnail((300, 200))
except IOError:
pass # handle exception
thumb_buffer = StringIO.StringIO()
image.save(thumb_buffer, format=image.format)
s3_thumb = default_storage.open('my_new_300x200_image.jpg', 'w')
s3_thumb.write(thumb_buffer.getvalue())
s3_thumb.close()

Categories

Resources