Skip to content

Commit e704e85

Browse files
committedOct 8, 2012
initial commit
0 parents  commit e704e85

File tree

7 files changed

+332
-0
lines changed

7 files changed

+332
-0
lines changed
 

Diff for: ‎LICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2012, natcam.pl <hello@natcam.pl>
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* Neither the name of the <organization> nor the
12+
names of its contributors may be used to endorse or promote products
13+
derived from this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Diff for: ‎MANIFEST.in

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include README.md
2+

Diff for: ‎README.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
What is it?
2+
===========
3+
4+
django-datatables-view is a base view for handling server side processing for the awesome datatables (http://datatables.net).
5+
6+
django-datatables-view simplifies handling of sorting, filtering and creating JSON output, as defined at:
7+
http://datatables.net/usage/server-side
8+
9+
10+
Usage
11+
=====
12+
13+
1. pip install django-datatables-view
14+
15+
2. views.py:
16+
17+
:::python
18+
19+
from django_datatables_view.base_datatable_view import BaseDatatableView
20+
21+
class OrderListJson(BaseDatatableView):
22+
# define column names that will be used in sorting
23+
# order is important and should be same as order of columns
24+
# displayed by datatables. For non sortable columns use empty
25+
# value like ''
26+
order_columns = ['number', 'user', 'state']
27+
28+
def get_initial_queryset(self):
29+
# return queryset used as base for futher sorting/filtering
30+
# these are simply objects displayed in datatable
31+
return MyModel.objects.all()
32+
33+
def filter_queryset(self, qs):
34+
# use request parameters to filter queryset
35+
36+
# simple example:
37+
sSearch = self.request.POST.get('sSearch', None)
38+
if sSearch:
39+
qs = qs.filter(name__istartswith=sSearch)
40+
41+
# more advanced example
42+
filter_customer = self.request.POST.get('customer', None)
43+
44+
if filter_customer:
45+
customer_parts = filter_customer.split(' ')
46+
qs_params = None
47+
for part in customer_parts:
48+
q = Q(customer_firstname__istartswith=part)|Q(customer_lastname__istartswith=part)
49+
qs_params = qs_params | q if qs_params else q
50+
qs = qs.filter(qs_params)
51+
return qs
52+
53+
def prepare_results(self, qs):
54+
# prepare list with output column data
55+
# queryset is already paginated here
56+
json_data = []
57+
for item in qs:
58+
json_data.append([
59+
item.number,
60+
"%s %s" % (item.customer_firstname, item.customer_lastname),
61+
item.get_state_display(),
62+
item.created.strftime("%Y-%m-%d %H:%M:%S"),
63+
item.modified.strftime("%Y-%m-%d %H:%M:%S")
64+
])
65+
return json_data
66+
67+
3. urls.py
68+
69+
::: python
70+
71+
# ...
72+
url(r'^my/datatable/data/$', login_required(OrderListJson.as_view()), name='order_list_json'),
73+
# ....
74+
75+
4. Define HTML + JavaScript part as usual, eg:
76+
::: javascript
77+
78+
$(document).ready(function() {
79+
var oTable = $('.datatable').dataTable({
80+
// ...
81+
"bProcessing": true,
82+
"bServerSide": true,
83+
"sAjaxSource": "{% url order_list_json %}"
84+
});
85+
// ...
86+
});

Diff for: ‎django_datatables_view/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def pretty_print(qs, colname='name'):
2+
out = qs.values_list(colname, flat=True)
3+
return ', '.join(out)
4+
5+
def datatables_sort(request, columns):
6+
sortdir = request.REQUEST.get('sSortDir_0', 'asc')
7+
i_sort_col = request.REQUEST.get('iSortCol_0', 0)
8+
9+
sdir = sortdir == 'desc' and '-' or ''
10+
11+
try:
12+
i_sort_col = int(i_sort_col)
13+
except Exception:
14+
i_sort_col = 0
15+
sortcol = columns[i_sort_col]
16+
return ['%s%s' % (sdir, sortcol)]

Diff for: ‎django_datatables_view/base_datatable_view.py

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# -*- coding: utf-8 -*-
2+
from .mixins import JSONResponseView
3+
4+
5+
class BaseDatatableView(JSONResponseView):
6+
""" JSON data for datatables
7+
"""
8+
order_columns = []
9+
10+
def initialize(*args, **kwargs):
11+
pass
12+
13+
def get_order_columns(self):
14+
""" Return list of columns used for ordering
15+
"""
16+
return self.order_columns
17+
18+
def ordering(self, qs):
19+
""" Get parameters from the request and prepare order by clause
20+
"""
21+
request = self.request
22+
# Number of columns that are used in sorting
23+
try:
24+
i_sorting_cols = int(request.REQUEST.get('iSortingCols', 0))
25+
except ValueError:
26+
i_sorting_cols = 0
27+
28+
order = []
29+
30+
for i in range(i_sorting_cols):
31+
# sorting column
32+
try:
33+
i_sort_col = int(request.REQUEST.get('iSortCol_%s' % i))
34+
except ValueError:
35+
i_sort_col = 0
36+
# sorting order
37+
s_sort_dir = request.REQUEST.get('sSortDir_%s' % i)
38+
39+
sdir = '-' if s_sort_dir == 'desc' else ''
40+
41+
sortcol = self.get_order_columns()[i_sort_col]
42+
if isinstance(sortcol, list):
43+
for sc in sortcol:
44+
order.append('%s%s' % (sdir, sc))
45+
else:
46+
order.append('%s%s' % (sdir, sortcol))
47+
if order:
48+
return qs.order_by(*order)
49+
return qs
50+
51+
def paging(self, qs):
52+
""" Paging
53+
"""
54+
limit = min(int(self.request.POST.get('iDisplayLength', 10)), 100)
55+
start = int(self.request.POST.get('iDisplayStart', 0))
56+
offset = start + limit
57+
return qs[start:offset]
58+
59+
# TO BE OVERRIDEN
60+
def get_initial_queryset(self):
61+
raise Exception("Method get_initial_queryset not defined!")
62+
63+
def filter_queryset(self, qs):
64+
return qs
65+
66+
def prepare_results(self, qs):
67+
return []
68+
# /TO BE OVERRIDEN
69+
70+
def get_context_data(self, *args, **kwargs):
71+
request = self.request
72+
self.initialize(*args, **kwargs)
73+
74+
qs = self.get_initial_queryset()
75+
76+
# number of records before filtering
77+
total_records = qs.count()
78+
79+
qs = self.filter_queryset(qs)
80+
81+
# number of records after filtering
82+
total_display_records = qs.count()
83+
84+
qs = self.ordering(qs)
85+
qs = self.paging(qs)
86+
87+
# prepare output data
88+
aaData = self.prepare_results(qs)
89+
90+
ret = {'sEcho': int(request.REQUEST.get('sEcho', 0)),
91+
'iTotalRecords': total_records,
92+
'iTotalDisplayRecords': total_display_records,
93+
'aaData': aaData
94+
}
95+
96+
return ret

Diff for: ‎django_datatables_view/mixins.py

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import sys
2+
3+
from django.http import HttpResponse
4+
from django.utils import simplejson
5+
from django.core.mail import mail_admins
6+
from django.utils.translation import ugettext as _
7+
from django.utils.cache import add_never_cache_headers
8+
from django.views.generic.base import TemplateView
9+
10+
11+
class JSONResponseMixin(object):
12+
is_clean = False
13+
14+
def render_to_response(self, context):
15+
""" Returns a JSON response containing 'context' as payload
16+
"""
17+
return self.get_json_response(context)
18+
19+
def get_json_response(self, content, **httpresponse_kwargs):
20+
""" Construct an `HttpResponse` object.
21+
"""
22+
response = HttpResponse(content,
23+
content_type='application/json',
24+
**httpresponse_kwargs)
25+
add_never_cache_headers(response)
26+
return response
27+
28+
def post(self, *args, **kwargs):
29+
return self.get(*args, **kwargs)
30+
31+
def get(self, request, *args, **kwargs):
32+
self.request = request
33+
response = None
34+
35+
try:
36+
func_val = self.get_context_data(**kwargs)
37+
if not self.is_clean:
38+
assert isinstance(func_val, dict)
39+
response = dict(func_val)
40+
if 'result' not in response:
41+
response['result'] = 'ok'
42+
else:
43+
response = func_val
44+
except KeyboardInterrupt:
45+
# Allow keyboard interrupts through for debugging.
46+
raise
47+
except Exception as e:
48+
# Mail the admins with the error
49+
exc_info = sys.exc_info()
50+
subject = 'JSON view error: %s' % request.path
51+
try:
52+
request_repr = repr(request)
53+
except Exception as exc:
54+
request_repr = 'Request repr() unavailable'
55+
import traceback
56+
message = 'Traceback:\n%s\n\nRequest:\n%s' % (
57+
'\n'.join(traceback.format_exception(*exc_info)),
58+
request_repr,
59+
)
60+
mail_admins(subject, message, fail_silently=True)
61+
62+
# Come what may, we're returning JSON.
63+
if hasattr(e, 'message'):
64+
msg = e.message
65+
msg += str(e)
66+
else:
67+
msg = _('Internal error')+': '+str(e)
68+
response = {'result': 'error',
69+
'text': msg}
70+
71+
json = simplejson.dumps(response)
72+
return self.render_to_response(json)
73+
74+
75+
class JSONResponseView(JSONResponseMixin, TemplateView):
76+
pass

Diff for: ‎setup.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from setuptools import setup, find_packages
2+
import os
3+
4+
version = '1.0'
5+
6+
here = os.path.abspath(os.path.dirname(__file__))
7+
README = open(os.path.join(here, 'README.md')).read()
8+
9+
setup(name='django-datatables-view',
10+
version=version,
11+
description='Django datatables view',
12+
long_description=README,
13+
url='https://bitbucket.org/pigletto/django-datatables-view',
14+
classifiers=[
15+
'Environment :: Web Environment',
16+
'Framework :: Django',
17+
'License :: OSI Approved :: BSD License',
18+
'Operating System :: OS Independent',
19+
'Programming Language :: Python',
20+
],
21+
keywords='django datatables view',
22+
author='Maciej Wisniowski',
23+
author_email='maciej.wisniowski@natcam.pl',
24+
license='BSD',
25+
packages=find_packages(exclude=['ez_setup']),
26+
include_package_data=True,
27+
zip_safe=False,
28+
dependency_links=[],
29+
install_requires=[
30+
'setuptools',
31+
],
32+
)

0 commit comments

Comments
 (0)
Please sign in to comment.