Namaste Everyone. I recently wrote an excel dump service as part of my work and wants to share that experience with you. Initially, I irritated a lot because creating excel in python is fairly easy but downloading it from javascript as an Ajax response is very painful. You should know how to set the right response headers, how to perform XHR request etc. I start with a sample project and explain the technique which I successfully used to solve the problem in a step by step way. Here we use a library called XLSX writer for building excel files.
Most of the times data export can be done through CSV, but if we need to dump data to an advanced data format, excel is the right one. We can add many worksheets to a workbook in Excel. Remember that CSV is plain text but a .XLSX(excel) file is binary one. We cannot create excel files from client-side since browser won’t let you mess up with the file system.
One approach is to create a physical excel file and store it in S3. Then we can pass the S3 file link back to UI and pass it as href to the button. But if we need to serve excel file without creating them on disk, then this technique can help you and you can proceed down.
At any point of time if you feel stuck, refer code from here https://github.com/narenaryan/Django-xdump
ObjectiveOur main goal is to request an Excel file from Django server and download it through the browser.
We can either do a GET or POST request. POST request is used to supply filters to create the file on the server. Here for simplicity I just do a GET request to fetch a file create a button on the UI and when a user clicks it, data should be exported to xlsx file and will download through the browser’s downloader.Now I am creating a fresh project with the configuration of Python3, Django 1.9 & Sqlite3. Install Django and XlsxWriter using pip.
$ pip install django==1.9 xlsxwriterNow let us create the project, app and modify necessary files.
$ django-admin startproject xdump$ django-admin startapp base
For showing the minimal UI, I am creating a homepage with a button. Before that we need to include our app in settings, migrate. Create templates folder and add it in DIRS in settings.
# In the project root$ mkdir templates
Now modify settings.py to add our app and templates folder configuration.
# Add base to installed appsINSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'base'
]
# Add templates in DIRS list
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ["templates"],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Let us do DB migration with this command. It is to create user tables in Django. After migration create super user.
# From project root$ python manage.py migrate
$ python manage.py createsuperuser
By this, we are able to start our adventure. We can create a service to dump all Django auth user details. Instead of user model you can dump data from any other models as per your requirement.
Now run Django server and add few users from admin panel. I added four users Manoj, Nikhil, Ram and Sai Kiran.
My UI template is so simple. I will just create a view to render the template. Template will have a button. If we click the button, all users details will be prepared and downloaded as a xlsx file, fairly simple. Isn’t it?
For any Django project, there will be a base HTML file that acts as a parent for extending by other templates. Here I define one such template called templates/base.html.
{% load staticfiles %}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auth Demo</title>
<!-- Core CSS - Include with every page -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a href="../" class="navbar-brand">Xdump</a>
</div>
</div>
</div>
{% block content %}
{% endblock %}
{% block javascript %}
{% endblock %}
</body>
</html> Now I create one more template for holding home view and it extends base template. File is templates/home.html {% extends 'base.html' %}
{% load staticfiles %}
{% block content %}
<div class="container">
<div class="row">
<div class="jumbotron">
<div class="row">
<center>
<h1>Hello</h1>
<p>Dump your data here</p>
<a href="javascript:void(0)" class="btn btn-primary btn-lg dump">Download</a>
</center>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'js/home.js' %}"></script>
{% endblock %}
home.js is the file where we are going to write logic for requesting the excel file and downloading it in browser. For now create a file in /static/js/home.js . static folder should be in the root directory. I added the templates but need Django views to power them. So I write views here. /base/views.py
from django.contrib.auth import get_user_modelfrom django.http import JsonResponse, HttpResponse
from django.views.generic import View
from django.shortcuts import render
from io import BytesIO
import xlsxwriter
# Create your views here.
class Home(View):
template_name = "home.html"
def get(self, request):
return render(request,self.template_name,{})
class Userdump(View):
def get(self, request):
output = BytesIO()
# Feed a buffer to workbook
workbook = xlsxwriter.Workbook(output)
worksheet = workbook.add_worksheet("users")
users = get_user_model().objects.all()
bold = workbook.add_format({'bold': True})
columns = ["user", "name", "email", "date_joined"]
# Fill first row with columns
row = 0
for i,elem in enumerate(columns):
worksheet.write(row, i, elem, bold)
row += 1
# Now fill other rows with columns
for user in users:
worksheet.write(row, 0, user.id)
worksheet.write(row, 1, user.username)
worksheet.write(row, 2, user.email)
worksheet.write(row, 3, user.date_joined.ctime())
row += 1
# Close workbook for building file
workbook.close()
output.seek(0)
response = HttpResponse(output.read(), content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
return response If you observe in the views, I just created a tem