Skip to content

Commit 96e9102

Browse files
authoredDec 14, 2024
Merge pull request #1 from birddevelper/feat/generate-from-queryset
Feat/generate from queryset
2 parents d25216a + 1e3d564 commit 96e9102

File tree

4 files changed

+152
-87
lines changed

4 files changed

+152
-87
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
django_query_to_table/__pycache__/*
2+
django_query_to_table.egg-info/*

‎README.md

+37-8
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
You can read more about this package here : [django query to table package](https://mshaeri.com/blog/generate-html-table-report-from-sql-query-in-django/)
66

7-
The package contains one function named "generateFromSql" accepting 12 arguments :
7+
The package contains two functions named:
8+
- **generate_from_sql**: Generate HTML table by given SQL query
9+
- **generate_from_queryset**:Generate HTML table by given Django queryset
10+
11+
Parameters:
812

9-
* cursor : DB cursor
1013
* title : The title of the report that will be shown on top of table
11-
* sqltext : The sql select query to retrieve data
14+
* sqltext/queryset : The sql select query to retrieve data / django queryset
1215
* footerCols : A list of columns name that you want to have Sum of values on footer . Example : ['amount','price']
1316
* htmlClass : Html CSS classes for the table
1417
* direction (default = "ltr") : Indicates direction of the report page. "ltr"- Left to Right , "rtl" - Right to Left
@@ -28,17 +31,16 @@ Run following command to install DjangoQtt :
2831
pip install django-query-to-table
2932
```
3033

31-
## Usage :
34+
## Example usage :
3235

36+
- Generate HTML table by SQL query:
3337

3438
```python
35-
from django.db import connection
3639
from django_query_to_table import DjangoQtt
3740
from django.http import HttpResponse
3841

3942
# view function in Django project
4043
def listOfPersons(request):
41-
cursor = connection.cursor()
4244
reportTitle = "Employee List"
4345
sqlQuery = "SELECT FirstName as 'First Name', LastName as 'Last Name', phone as 'Phone Number', salary as 'Salary' FROM persons"
4446
columnsToBeSummarized = ['Salary']
@@ -48,13 +50,40 @@ def listOfPersons(request):
4850
evenRowsBackgroundColor = '#ffeeff'
4951
oddRowsBackgroundColor = '#ffffff'
5052
rowIndexVisibility = True
51-
table = DjangoQtt.generateFromSql(cursor, reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
53+
table = DjangoQtt.generate_from_sql(reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
5254
"ltr", fontName, "Total Salary", rowIndexVisibility,
5355
headerRowBackgroundColor, evenRowsBackgroundColor, oddRowsBackgroundColor
5456
)
5557

56-
# here the table is a string variable contianing the html table showing the query result
58+
# here the table is a string variable containing the html table showing the query result
5759
return HttpResponse(table)
5860

5961
```
6062

63+
- Generate HTML table from querset:
64+
65+
```python
66+
from django_query_to_table import DjangoQtt
67+
from django.http import HttpResponse
68+
from .models import Order
69+
# view function in Django project
70+
def listOfPersons(request):
71+
72+
order_queryset = Order.objects.all().values("customer", "product", "amount")
73+
reportTitle = "Order List"
74+
columnsToBeSummarized = ['amount']
75+
fontName = "Arial"
76+
cssClasses = "reportTable container"
77+
headerRowBackgroundColor = '#ffeeee'
78+
evenRowsBackgroundColor = '#ffeeff'
79+
oddRowsBackgroundColor = '#ffffff'
80+
rowIndexVisibility = True
81+
table = DjangoQtt.generate_from_queryset(reportTitle, order_queryset, columnsToBeSummarized, cssClasses,
82+
"ltr", fontName, "Total amount", rowIndexVisibility,
83+
headerRowBackgroundColor, evenRowsBackgroundColor, oddRowsBackgroundColor
84+
)
85+
86+
# here the table is a string variable containing the html table showing the queryset result
87+
return HttpResponse(table)
88+
89+
```

‎django_query_to_table/DjangoQtt.py

+111-77
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,114 @@
1+
import traceback
2+
import warnings
13
from django.template import Template, Context
4+
from django.db import connection
5+
import logging
26

3-
import traceback
7+
logger = logging.getLogger(__name__)
8+
9+
def _validate_sql(sqltext):
10+
"""Validate the SQL query."""
11+
if len(sqltext) < 8 or ("select" not in sqltext.lower()):
12+
return False
13+
return True
14+
15+
def _execute_query(cursor, sqltext):
16+
"""Execute the SQL query and fetch data."""
17+
cursor.execute(sqltext)
18+
desc = cursor.description
19+
result_as_list = [
20+
dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
21+
]
22+
columns = [col[0] for col in desc]
23+
return columns, result_as_list
24+
25+
def _calculate_sums(data, columns, footerCols, totalText):
26+
"""Calculate sum for specified columns."""
27+
sumOfColumn = {col: 0 if col in footerCols else "-" for col in columns}
28+
29+
for d in data:
30+
for attr, value in d.items():
31+
if attr in footerCols:
32+
sumOfColumn[attr] += float(str(value).replace(",", ""))
33+
34+
totalColumnSet = False
35+
for col in sumOfColumn:
36+
if sumOfColumn[col] != "-":
37+
sumOfColumn[col] = format(float(str(sumOfColumn[col])), ",")
38+
elif not totalColumnSet:
39+
sumOfColumn[col] = totalText
40+
totalColumnSet = True
41+
42+
return sumOfColumn
43+
44+
def _render_template(context):
45+
"""Render the HTML table template."""
46+
template = (
47+
"<center><table dir=\"{{direction}}\" border=\"1\" class=\"table table-striped {{htmlClass}}\" "
48+
"style=\"width:93%;font-family:'{{font}}'\">"
49+
" <thead> <tr> <th colspan='{{columns|length|add:'1'}}' style=\"font-family:'{{font}}';font-weight: bold;\" > {{title}} </th> </tr>"
50+
" <tr style='background-color:{{headerRowColor}}'>{% if rowIndex %} <th align=\"center\"> # </th> {% endif %} {% for c in columns %} <th>{{ c }}</th> {% endfor %} </tr> </thead>"
51+
" <tbody> {% for d in data %} <tr style='background-color:{% if forloop.counter0|divisibleby:'2' %} {{evenRowColor}} {% else %} {{oddRowColor}} {% endif %} ' >"
52+
" {% if rowIndex %} <td align=\"center\">{{ forloop.counter }}</td> {% endif %} {% for attr, value in d.items %} <td align=\"center\">{{ value }}</td> {% endfor %} </tr> {% endfor %} "
53+
" {% if sumOfColumn %} <tr style='background-color:#eee;font-weight: bold;'> <td></td> {% for a,v in sumOfColumn.items %} <td align=\"center\">{{ v }}</td> {% endfor %} </tr> {% endif %}</tbody> </table></center>"
54+
)
55+
56+
return Template(template).render(Context(context))
57+
58+
def _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor):
59+
"""Internal method to generate the SQL report."""
60+
columns, data = _execute_query(cursor, sqltext)
61+
62+
sumOfColumn = None
63+
if footerCols:
64+
sumOfColumn = _calculate_sums(data, columns, footerCols, totalText)
65+
66+
context = {
67+
'title': title,
68+
'data': data,
69+
'columns': columns,
70+
'sumOfColumn': sumOfColumn,
71+
'direction': direction,
72+
'font': font,
73+
'totalText': totalText,
74+
'rowIndex': rowIndex,
75+
'headerRowColor': headerRowColor,
76+
'evenRowColor': evenRowColor,
77+
'oddRowColor': oddRowColor,
78+
'htmlClass': htmlClass
79+
}
80+
81+
return _render_template(context)
82+
83+
def generate_from_sql(title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
84+
"""Generate the SQL report with an internally created cursor."""
85+
try:
86+
if not _validate_sql(sqltext):
87+
return 'Not Valid SQL'
88+
89+
with connection.cursor() as cursor:
90+
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
91+
except Exception as e:
92+
logger.error(traceback.format_exc())
93+
return f"Error: {str(e)}"
94+
95+
def generateFromSql(cursor, title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
96+
"""Generate the SQL report with a provided cursor."""
97+
warnings.warn("generateFromSql is deprecated and will be removed in a future release. Use generate_from_sql instead.", DeprecationWarning)
98+
try:
99+
if not _validate_sql(sqltext):
100+
return 'Not Valid SQL'
4101

5-
def generateFromSql(cursor, title, sqltext, footerCols= None, htmlClass="", direction="ltr", font="Tahoma", totalText = "Total", rowIndex = False, headerRowColor ='#eeeeee' ,evenRowColor = '#ffffff', oddRowColor="#ffffff") :
6-
sumCols=[]
7-
try :
8-
if(len(sqltext) < 8 or ("select" not in sqltext.lower())) :
9-
return ('Not Valid SQL')
10-
11-
sql_query = sqltext
12-
sumCols=footerCols
13-
# execute sql query and retrieve data from db
14-
cursor.execute(sql_query)
15-
16-
# retrieve columns of the data
17-
desc = cursor.description
18-
19-
result_as_list = [
20-
dict(zip([col[0] for col in desc ], row)) for row in cursor.fetchall()
21-
]
22-
23-
columns = [col[0] for col in desc ] #result.keys()
24-
data = result_as_list
25-
26-
27-
28-
29-
30-
sumOfColumn = None
31-
if(sumCols != None) :
32-
sumOfColumn={}
33-
34-
# initiating footer aggrigation values
35-
for c in columns :
36-
if(c in sumCols):
37-
sumOfColumn[c]=0
38-
else:
39-
sumOfColumn[c]="-"
40-
41-
# travers in rows and aggrigate the values of columns those are in footerCols
42-
for d in data :
43-
for attr, value in dict(d).items() :
44-
if(attr in sumCols):
45-
sumOfColumn[attr]=sumOfColumn[attr]+float(str(value).replace(",", ""))
46-
47-
totalColumnSet = False
48-
if(sumCols != None) :
49-
for col, val in sumOfColumn.items() :
50-
if(val!="-") :
51-
sumOfColumn[col] = format(float(str(val)),",")
52-
elif (totalColumnSet == False) :
53-
sumOfColumn[col] = totalText
54-
totalColumnSet = True
55-
56-
# template to generate data from data retrieved from database
57-
template= "<center><table dir=\"{{direction}}\" border=\"1\" class=\"table table-striped {{htmlClass}}\" style=\"width:93%;font-family:'{{font}}'\"> <thead> <tr> <th colspan='{{columns|length|add:'1'}}' style=\"font-family:'{{font}}';font-weight: bold;\" > {{title}} </th> </tr> <tr style='background-color:{{headerRowColor}}'>{% if rowIndex == True %} <th align=\"center\"> # </th> {% endif %} {% for c in columns %} <th>{{ c }}</th> {% endfor %} </tr> </thead> <tbody> {% for d in data %} <tr style='background-color:{% if forloop.counter0|divisibleby:'2' %} {{evenRowColor}} {% else %} {{oddRowColor}} {% endif %} ' > {% if rowIndex == True %} <td align=\"center\">{{ forloop.counter }}</td> {% endif %} {% for attr, value in d.items %} <td align=\"center\">{{ value }}</td> {% endfor %} </tr> {% endfor %} {% if sumOfColumn != None %} <tr style='background-color:#eee;font-weight: bold;'> <td></td> {% for a,v in sumOfColumn.items %} <td align=\"center\">{{ v }}</td> {% endfor %} </tr> {% endif %}</tbody> </table></center>"
58-
59-
60-
61-
c = Context({
62-
'title':title,
63-
'data':data,
64-
'columns':columns,
65-
'sumOfColumn':sumOfColumn,
66-
'direction':direction,
67-
'font':font,
68-
'totalText':totalText,
69-
'rowIndex' : rowIndex,
70-
'headerRowColor' :headerRowColor ,
71-
'evenRowColor' : evenRowColor,
72-
'oddRowColor': oddRowColor,
73-
'htmlClass' : htmlClass
74-
})
75-
76-
return Template(template).render(c)
77-
except BaseException as e :
78-
#print exception trace to console
79-
print(traceback.format_exc())
80-
return ("Error :" + str(e))
102+
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
103+
except Exception as e:
104+
logger.error(traceback.format_exc())
105+
return f"Error: {str(e)}"
106+
107+
def generate_from_queryset(title, queryset, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
108+
"""Generate the SQL report using a Django QuerySet."""
109+
try:
110+
sqltext = str(queryset.query)
111+
return generate_from_sql(title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
112+
except Exception as e:
113+
logger.error(traceback.format_exc())
114+
return f"Error: {str(e)}"

‎setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
setup_args = dict(
88
name='django-query-to-table',
9-
version='0.1.9',
10-
description='A simple to use Django package to turn your sql query into a beautiful reporting html table',
9+
version='1.0.0',
10+
description='A simple to use Django package to turn your queryset and sql query into a beautiful reporting html table',
1111
long_description_content_type="text/markdown",
1212
long_description=README,
1313
license='GNU',

0 commit comments

Comments
 (0)
Please sign in to comment.