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()
Related
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)
I have a Django Rest Framework view that generates python-docx objects, and I want to save such files in a model. I read in the documentation of python-docx that you can specify a "file-like" object to save the object, but I don't understand what it means.
This is my view:
class RoomingWordView(viewsets.ViewSet):
def list(self, request, *args, **kwargs):
... some code
documents_to_return = self.get_hotel_document(start_date, end_date, confirmed, hotel, bookings)
return Response(documents_to_return)
def get_hotel_document(self, start_date, end_date, confirmed, hotel, bookings):
from django.core.files import File
from docx import Document
from docx.shared import Inches, Pt
document = Document()
section = document.sections[-1]
section.left_margin = Inches(0.5)
section.right_margin = Inches(0.5)
style = document.styles['Normal']
font = style.font
font.name ='Arial'
font.size = Pt(10)
document.add_heading("MY COMPANY")
if confirmed:
document.add_paragraph("ROOMING LIST DEL 01-12-2018 AL 31-01-2019 INCLUYE RESERVAS CONFIRMADAS")
else:
document.add_paragraph("ROOMING LIST DEL 01-12-2018 AL 31-01-2019")
document.add_paragraph("Hotel: {}".format(hotel))
table = document.add_table(rows=len(bookings), cols=10)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Booking'
hdr_cells[1].text = 'Reservado a'
hdr_cells[2].text = '# Pax'
hdr_cells[3].text = 'Agencia'
hdr_cells[4].text = 'Habs'
hdr_cells[5].text = 'Hab./Plan'
hdr_cells[6].text = 'Entrada'
hdr_cells[7].text = 'Salida'
hdr_cells[8].text = 'Confirmación'
hdr_cells[9].text = 'Producción'
for cell in table.rows[0].cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
run.underline = True
for booking in bookings['bookings']:
row_cells = table.add_row().cells
row_cells[0].text = booking['booking']
row_cells[1].text = "\n".join(booking['people'])
row_cells[2].text = booking['pax']
row_cells[3].text = booking['agency']
row_cells[4].text = booking['rooms']
row_cells[5].text = "{}\n{}".format(booking['room_type'], booking['plan_type'])
row_cells[6].text = booking['check_in']
row_cells[7].text = booking['check_out']
row_cells[8].text = booking['confirmation']
row_cells[9].text = str(booking['production'])
for row in table.rows:
for cell in row.cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
font = run.font
font.size = Pt(8)
file_object = "rooming_reports/Rooming {} {}-{}.docx".format(hotel, start_date, end_date)
document.save(file_object)
return file_object
I want the view to save the resulting document object in a model called RoomingWordDocument, instead of saving it as a file, but I don't know how to assign document variable to RoomingWordDocument.file field.
Assuming you have a model as
class RoomingWordDocument(models.Model):
doc = models.FileField(null=True)
name = models.CharField(max_length=50)
Then try this snippet
class RoomingWordView(viewsets.ViewSet):
def list(self, request, *args, **kwargs):
# your list method
# return Response(documents_to_return)
def get_hotel_document(self, start_date, end_date, confirmed, hotel, bookings):
# your code
# ......
file_object = "rooming_reports/Rooming {} {}-{}.docx".format(hotel, start_date, end_date)
document.save(file_object)
# here is the new snippet
# found a simple way now
model_object = RoomingWordDocument.objects.create(name="somename")
model_object.doc.name = file_object
model_object.save() # this is importent
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from django.core.files import File
fp = open(file_object, 'rb')
model_object = RoomingWordDocument.objects.create(name="somename")
model_object.doc.save(file_object, File(fp))
return Response("some response")
Reference
1. StringIo in Python2.X and 3.X
2. How to create a file and save it to a model's FileField?
3. Set Django's FileField to an existing file
I'm using django and generating reports following this example, I need to generate a last page but without headers or footers and different content.
I'm trying to do this:
def print_example(self):
buffer = self.buffer
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
elements = []
elements.append(Paragraph('Content for all pages'), my_custom_style)
# ...
doc.build(elements, onFirstPage=self._header_footer, onLaterPages=self._header_footer,
canvasmaker=NumberedCanvas)
doc2 = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=self.pagesize)
elements2 = []
elements2.append(Paragraph('Content for the last page only'), my_custom_style)
doc2.build(elements2, canvasmaker=NumberedCanvas)
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
Then only the last content appears and previous content dissapears.
How can I generate the last page with different content?
I don't think it's possible using SimpleDocTemplate but you can achieve this by using BaseDocTemplate and defining your own templates.
Basic example
from reportlab.platypus import PageTemplate, BaseDocTemplate, NextPageTemplate, PageBreak
def headerFooterLayout(canvas, doc):
canvas.saveState()
canvas.setPageSize(self.pagesize)
# add header/footer
canvas.restoreState()
def emptyLayout(canvas, doc):
canvas.saveState()
canvas.setPageSize(self.pagesize)
canvas.restoreState()
pHeight, pWidth = self.pagesize
myFrame = Frame(0, 0, pHeight, pWidth, id='myFrame')
headerFooterTemplate = PageTemplate(id='headerFooterTemplate',
frames=[myFrame],
onPage=headerFooterLayout)
emptyTemplate = PageTemplate(id='emptyTemplate',
frames=[myFrame],
onPage=emptyLayout)
elements = []
elements.append(Paragraph('blah', style))
elements.append(NextPageTemplate('emptyTemplate'))
elements.append(PageBreak())
elements.append(Paragraph('last page', style))
doc = BaseDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72)
doc.addPageTemplates([headerFooterTemplate, emptyTemplate])
doc.build(elements)
It's been quite a while since I used this so there may well be some issues but comment if something doesn't work.
This is all in the user guide but can be hard to find what you're looking for.
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()
How can I make simple table in ReportLab? I need to make a simple 2x20 table and put in some data. Can someone point me to an example?
The simplest table function:
table = Table(data, colWidths=270, rowHeights=79)
How many columns & end rows depend from tuple of data. All our table functions looks like:
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus.tables import Table
cm = 2.54
def print_pdf(modeladmin, request, queryset):
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'
elements = []
doc = SimpleDocTemplate(response, rightMargin=0, leftMargin=6.5 * cm, topMargin=0.3 * cm, bottomMargin=0)
data=[(1,2),(3,4)]
table = Table(data, colWidths=270, rowHeights=79)
elements.append(table)
doc.build(elements)
return response
This will make table 2X2, and fill it with numbers 1,2,3,4. Then you can make file document. In my case i made HttpResponse what is pretty the same like file.
Just an add on to radtek and Pol's answer:
You can substitute the response argument to SimpleDocTemplate() with a buffer object like io.BytesIO() like so:
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus.tables import Table
import io
cm = 2.54
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, rightMargin=0, leftMargin=6.5 * cm, topMargin=0.3 * cm, bottomMargin=0)
... # To be continued below
This could be useful in cases when you want to convert the PDF object into bytes and then into byte-string to be sent in JSON format:
... # Continuation from above code
buffer.seek(0)
buffer_decoded = io.TextIOWrapper(buffer, encoding='utf-8', errors='ignore').read()
return JsonResponse({
"pdf_bytes": buffer_decoded,
})
Taken from the doc (https://www.reportlab.com/docs/reportlab-userguide.pdf):
The required filename can be a string, the name of a file to receive the created PDF document; alternatively it can be an object which has a write method such as aBytesIO or file or socket