Skip to content

Commit

Permalink
Merge pull request #1 from francesco-filicetti/master
Browse files Browse the repository at this point in the history
unit tests + example app + refactor
  • Loading branch information
peppelinux authored Jan 17, 2020
2 parents 5fb8fe0 + 4396b58 commit 63181c0
Show file tree
Hide file tree
Showing 21 changed files with 435 additions and 56 deletions.
15 changes: 15 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# .coveragerc to control coverage.py
[run]
omit =
*/__init__.py
*/admin.py
*/apps/*
*/distutils/*
*/example/*
/example_project/example_project/*
*/manage.py
*/migrations/*
*/settings.py
*/site-packages/*
*/src/*
*/urls.py
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
build/
dist/
*egg-info

example_project/
db.sqlite3
manage.py
.coverage
*__pycache__/
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,14 @@ urlpatterns = [
url(r'^datatable.json$', login_required(StatoUtenzaCorso_DTJson), name='datatable_json'),
]
````

Example app
-----------

````
cd example
./manage.py makemigrations
./manage.py migrate
./manage.py createsuperuser
./manage.py runserver
````
55 changes: 37 additions & 18 deletions datatables_ajax/datatables.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import datetime
import json

from abc import abstractmethod

from django.conf import settings
from django.utils import timezone

from . settings import *


class DjangoDatatablesServerProc(object):
def __init__(self, request, model, columns):
def __init__(self, request, queryset, columns):
"""
model = StatoUtenzaCorso
columns = ['pk', 'cliente', 'corso', 'altro',
'contattato_mediante', 'data_creazione', 'stato']
"""
self.columns = columns
self.model = model.objects if hasattr(model, 'objects') else model
# self.model = model.objects if hasattr(model, 'objects') else model
self.queryset = queryset
self.request = request
self.method = 'POST' if request.POST.get('args') else 'GET'
if self.method == 'POST':
Expand Down Expand Up @@ -66,21 +72,25 @@ def __init__(self, request, model, columns):
v = getattr(self, field)
setattr(self, field, int(v))

@abstractmethod
def get_queryset(self):
"""
Overload me.
Overload me in your DjangoDatatablesServerProc' inherited class!
The query must be customized to get it work
Example data:
if self.search_key:
self.aqs = self.model.objects.filter(
Q(cliente__nome__icontains=self.search_key) | \
Q(cliente__cognome__icontains=self.search_key) | \
Q(cliente__nominativo__icontains=self.search_key) | \
Q(corso__nome__istartswith=self.search_key) | \
Q(contattato_mediante__nome__istartswith=self.search_key) | \
Q(altro__nome__istartswith=self.search_key))
else:
self.aqs = self.model.objects.all()
"""
if self.search_key:
self.aqs = self.model.objects.filter(
Q(cliente__nome__icontains=self.search_key) | \
Q(cliente__cognome__icontains=self.search_key) | \
Q(cliente__nominativo__icontains=self.search_key) | \
Q(corso__nome__istartswith=self.search_key) | \
Q(contattato_mediante__nome__istartswith=self.search_key) | \
Q(altro__nome__istartswith=self.search_key))
else:
self.aqs = self.model.objects.all()
pass

def get_ordering(self):
"""
Expand Down Expand Up @@ -110,8 +120,11 @@ def get_paging(self):
self.fqs = self.aqs[self.start:self.start+self.lenght]

def _make_aware(self, dt):
if hasattr(dt, 'tzinfo') and dt.tzinfo != timezone.get_default_timezone():
return dt.astimezone(timezone.get_default_timezone())
if hasattr(dt, 'tzinfo'):
if dt.tzinfo != timezone.get_default_timezone():
return dt.astimezone(timezone.get_default_timezone())
else:
return dt
return timezone.make_aware(dt, timezone=timezone.get_current_timezone())

def _dt_strftime_as_naive(self, dt):
Expand All @@ -132,9 +145,15 @@ def fill_data(self):
v = getattr(r, e)
if v:
if isinstance(v, datetime.datetime):
vrepr = self._make_aware(v).strftime(settings.DEFAULT_DATETIME_FORMAT)
default_datetime_format = DEFAULT_DATETIME_FORMAT
if hasattr(settings, 'DEFAULT_DATETIME_FORMAT'):
default_datetime_format = settings.DEFAULT_DATETIME_FORMAT
vrepr = self._make_aware(v).strftime(default_datetime_format)
elif isinstance(v, datetime.date):
vrepr = v.strftime(settings.DEFAULT_DATE_FORMAT)
default_date_format = DEFAULT_DATE_FORMAT
if hasattr(settings, 'DEFAULT_DATE_FORMAT'):
default_date_format = settings.DEFAULT_DATE_FORMAT
vrepr = v.strftime(default_date_format)
elif callable(v):
vrepr = str(v())
else:
Expand All @@ -144,7 +163,7 @@ def fill_data(self):
cleaned_data.append(vrepr)

self.d['data'].append( cleaned_data )
self.d['recordsTotal'] = self.model.count()
self.d['recordsTotal'] = self.queryset.count()
self.d['recordsFiltered'] = self.aqs.count()

def get_dict(self):
Expand Down
3 changes: 3 additions & 0 deletions datatables_ajax/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
DEFAULT_DATETIME_FORMAT = '{} %H:%M'.format(DEFAULT_DATE_FORMAT)
DATE_INPUT_FORMATS = [DEFAULT_DATE_FORMAT, '%d/%m/%Y']
2 changes: 2 additions & 0 deletions example/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
default_app_config = 'example.apps.ExampleConfig'

6 changes: 6 additions & 0 deletions example/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ExampleConfig(AppConfig):
name = 'example'
verbose_name = "Example Application"
34 changes: 34 additions & 0 deletions example/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.0 on 2020-01-16 13:04

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Ticket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=255, unique=True)),
('subject', models.CharField(max_length=255)),
('description', models.TextField()),
('created', models.DateTimeField(auto_now=True)),
('priority', models.IntegerField(default=0)),
('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Ticket',
'verbose_name_plural': 'Ticket',
'ordering': ['priority', '-created', 'code'],
},
),
]
18 changes: 18 additions & 0 deletions example/migrations/0002_ticket_edited.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0 on 2020-01-17 08:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('example', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='ticket',
name='edited',
field=models.DateField(auto_now=True),
),
]
Empty file added example/migrations/__init__.py
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
33 changes: 33 additions & 0 deletions example/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.conf import settings
from django.db import models
from django.utils.translation import gettext as _


class Ticket(models.Model):
"""
Simple ticket model
"""
code = models.CharField(max_length=255, unique=True)
subject = models.CharField(max_length=255)
description = models.TextField()
created = models.DateTimeField(auto_now=True)
edited = models.DateField(auto_now=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True)
priority = models.IntegerField(default=0)

def get_priority_verbose(self):
"""
Pure example method
"""
if self.priority == 1:
return "High"
return "Normal"

class Meta:
ordering = ["priority",
"-created",
"code"]
verbose_name = _("Ticket")
verbose_name_plural = _("Ticket")
111 changes: 111 additions & 0 deletions example/templates/datatable_script.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{% load i18n %}
{% load static %}

<script charset="utf8" src="{% static 'js/datatables/datatables.min.js' %}"></script>
<script>
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
return results[1] || 0;
}
var url = "{{ item_url }}";
$(document).ready(function() {

var datatable = $('.datatable-responsive-serverside').DataTable({
dom: 'lrtip',
aLengthMenu: [
[10, 25, 50, 100, ], // -1],
[10, 25, 50, 100, ] //"All"]
],
paging: true,
//responsive: true,
processing: true,
serverSide: true,
columnDefs: [
// Checkbox column (PK)
{
visible: false,
orderable: false,
className: "select-checkbox",
targets: [ 0 ],
data: null,
defaultContent: "",
render: function ( data, type, row ) {
return "<input type='checkbox' name='ticket_id' value='"+row[0]+"' />";
}
} ,+
// Code column
{
targets: [ 1 ],
render: function ( data, type, row ) {
return "<a href='"+item_url+row[1]+"'>"+data+"</a>";
}
},
// Subject column
{
targets: [ 2 ],
orderable: true,
},
// Created column
{
targets: [ 3 ],
orderable: true,
},
// Edited column
{
targets: [ 4 ],
orderable: true,
},
// Description column
{
targets: [ 5 ],
orderable: false,
},
// Priority column
{
targets: [ 6 ],
orderable: false,
},
],
language: {
"emptyTable": "{% trans 'No records' %}",
"info": "{% trans 'Visible' %} _END_ {% trans 'of' %} _TOTAL_ {% trans 'total records' %}",
"infoEmpty": "{% trans 'Visible' %} 0 {% trans 'record' %}",
"infoPostFix": "",
"thousands": ",",
"lengthMenu": "{% trans 'Show' %} _MENU_",
"loadingRecords": "Loading...",
"processing": "Processing...",
"zeroRecords": "{% trans 'No record found' %}",
"paginate": {
"first": "{% trans 'First' %}",
"last": "{% trans 'Last' %}",
"next": "{% trans 'Next' %}",
"previous": "{% trans 'Previous' %}"
},
},
//ajax: '{{ ajax_url }}',
ajax: {
url: "{{ ajax_url }}",
method: "post",
data: function(args) {
return{
"args": JSON.stringify(args),
{% for k,v in request.GET.items %}
"{{ k }}": "{{ v }}",
{% endfor %}
};
}
},
});
// Search engine (select box with years and free text field)
// See templates/top_filters.html
$('#year, #search_box').on('change keyup', function () {
var year = $('#year').val();
var text = $('#search_box').val();
var args = '{"year": "'+year+'", "text": "'+text+'"}';
datatable.search(args).draw();
} );

});
</script>
2 changes: 2 additions & 0 deletions example/templates/table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% include 'top_filters.html' %}
{% include 'table_header.html' %}
21 changes: 21 additions & 0 deletions example/templates/table_header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% load i18n %}
{% load static %}

<form method="post" id="ticket_datatable_form">
<table class="table table-striped table-hover datatable-responsive-serverside">
<!--id="ticketTable"-->
<thead>
<tr>
<th></th>
<th>{% trans "Code" %}</th>
<th>{% trans "Subject" %}</th>
<th>{% trans "Description" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Created by" %}</th>
<th>{% trans "Edited" %}</th>
<th>{% trans "Priority" %}</th>
</tr>
</thead>
</table>
{% csrf_token %}
</form>
Loading

0 comments on commit 63181c0

Please sign in to comment.