Assumptions
- the method generate_cassettes() produces a small dataset that won't cause any memory issues.
- No models(and hence model instances) are being used.
Approach
- We have 2 views, namely generate_rna and download_csv. generate_rna points to myapp/(in my case, it can point to anything else as desirable in general) whereas download_csv points to myapp/download/csv(in my case, it can point to anything else as desirable in general).
- When you hit GET myapp/, generate_rna renders an empty form
- When you hit POST myapp/, generate_rna takes in the data submitted by the user, validates it, and if the data is valid, it calls generate_cassettes, where it creates the data, and then it renders a form with populated data, a table showing the data generated by generate_cassettes and also shows a "Download CSV" button
- When you hit POST myapp/download/csv with cassette data(generated by generate_cassettes) as payload, it creates a csv file out of the data(using python csv module and changing Content-Type and Content-Disposition headers) and serves it for download.
Here is some code
myapp urls.py
from django.urls import path
from .views import generate_rna, download_csv
urlpatterns = [
path('', generate_rna),
path('download/csv/', download_csv)
]
myapp forms.py
from django import forms
class RNAGeneratorForm(forms.Form):
name = forms.CharField(max_length=100)
price = forms.DecimalField(decimal_places=2)
def generate_cassettes(self):
#The behaviour of this function is not given in question and hence is assumed.
return [['Name', 'Price'], ['Cassette 1', '130'], ['Cassette 2', '140']]
RNA.html
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
}
</style>
<title> RNA Form </title>
</head>
<body>
<!-- This form takes input from user -->
<form action="/myapp/" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value = "Submit" />
</form>
{% if cassettes %}
<table>
<tr>
{% for column_name in cassettes.0 %}
<th> {{ column_name }} </th>
{% endfor %}
</tr>
{% for row in cassettes|slice:"1:" %}
<tr>
{% for column in row %}
<td> {{ column }} </td>
{% endfor %}
</tr>
{% endfor %}
</table>
<form action="/myapp/download/csv/" method="POST">
{% csrf_token %}
<input type="submit" value="Download CSV">
<input type="hidden" id="cassette-data" name="cassette-data">
</form>
{{ cassettes|json_script:"cassette-json" }}
<script>
document.getElementById("cassette-data").value = document.getElementById("cassette-json").textContent;
</script>
{% else %}
No Cassettes
{% endif %}
</body>
</html>
The Approach Used in RNA.html
- If cassettes exist(for example, if the server responds to a POST myapp/), I render the data as a table and show the "Download CSV button". If you see this link https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script,
by using json_script there is a way to expose an object in the form of json. I then have a script with id "cassette-json"(Please refer to this line in the html source {{ cassettes|json_script:"cassette-json" }}). I have another script tag in which i assign the cassettes as json to a hidden input(of name "cassette-data"). The "Download CSV" button is embedded in another form that sends "cassette-data" as payload to myapp/download/csv.
myapp views.py
from django.shortcuts import render, HttpResponse
from .forms import RNAGeneratorForm
import csv
import json
def generate_rna(request):
if request.method == 'POST':
form = RNAGeneratorForm(request.POST)
if form.is_valid():
result = form.generate_cassettes()
context = {
"form": form,
"cassettes": result
}
return render(request, 'myapp/RNA.html', context)
else:
form = RNAGeneratorForm()
context = {
"form": form
}
return render(request, 'myapp/RNA.html', context)
def download_csv(request):
if request.method == 'POST':
data = request.POST
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="cassettes.csv"'
csv_writer = csv.writer(response)
for row in json.loads(data['cassette-data']):
print(row)
csv_writer.writerow(row)
return response
Please reach out in case of any queries.