From 20d6a95b060a648270f5ecf48ff5f97b6f9cc6fd Mon Sep 17 00:00:00 2001 From: pankus Date: Fri, 25 Aug 2017 21:49:47 +0200 Subject: [PATCH 1/3] Select menu duplicates with yadcf --- datatables/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datatables/__init__.py b/datatables/__init__.py index 05e56c8..f1c1cf8 100644 --- a/datatables/__init__.py +++ b/datatables/__init__.py @@ -277,7 +277,7 @@ def _set_yadcf_data(self, query): query=query, exclude=i) v = filtered.add_columns(col.sqla_expr).distinct().all() self.yadcf_params.append( - ('yadcf_data_{:d}'.format(i), [r[0] for r in v])) + ('yadcf_data_{:d}'.format(i), [r[0] for r in set(v)])) def run(self): """Launch filtering, sorting and paging to output results.""" From d4d22ac45d9a7f9d2ab47da9c51e4a04b62936d6 Mon Sep 17 00:00:00 2001 From: pankus Date: Fri, 25 Aug 2017 22:22:26 +0200 Subject: [PATCH 2/3] Update __init__.py --- datatables/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datatables/__init__.py b/datatables/__init__.py index f1c1cf8..234fdfe 100644 --- a/datatables/__init__.py +++ b/datatables/__init__.py @@ -277,7 +277,7 @@ def _set_yadcf_data(self, query): query=query, exclude=i) v = filtered.add_columns(col.sqla_expr).distinct().all() self.yadcf_params.append( - ('yadcf_data_{:d}'.format(i), [r[0] for r in set(v)])) + ('yadcf_data_{:d}'.format(i), [r[0] for r in sorted(set(v))])) def run(self): """Launch filtering, sorting and paging to output results.""" From 6912cb2a5c2b739b55bbd9def8ec8ca5d872b349 Mon Sep 17 00:00:00 2001 From: "DESKTOP-PRBKMKF\\user" Date: Sat, 24 Nov 2018 19:07:41 +0100 Subject: [PATCH 3/3] natural sort natural sort method for PostgreSQL --- README.rst | 39 +++++++++++++++++++++++ datatables/__init__.py | 8 +++-- examples/flask_tut/flask_tut/__init__.py | 3 +- examples/pyramid_tut/pyramid_tut/views.py | 2 +- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index fb24bba..46d1225 100644 --- a/README.rst +++ b/README.rst @@ -8,6 +8,45 @@ It is framework agnostic, tested with `Pyramid `_ It only depends on SQLAlchemy, and is compatible with version **1.10.x** of DataTables. +**Small contribution:** +In this fork has been added a natural sort method for PostgreSQL models. It needs a custom function to be created in the db: + +.. code-block:: sql + + -- + -- This file replaces a previous one which is now available at + -- /junk/naturalsort-hack.sql which had certain drawbacks and + -- was far more complex. This approach is simpler, though it + -- has the drawback of not dealing with locales (all comparisons + -- end up made as if in C locale). + -- + -- To use: + -- + -- SELECT ... ORDER BY naturalsort(column); + -- + -- +optionally, + -- + -- CREATE INDEX ON yourtable (naturalsort(column)); + -- + -- (The basic method is to prefix each numeric substring with its + -- length, then sort as a bytea to get C locale and \x00 delimiters + -- between fragments) + -- + + create or replace function naturalsort(text) + returns bytea + language sql + immutable strict + as $f$ + select string_agg(convert_to(coalesce(r[2], + length(length(r[1])::text) || length(r[1])::text || r[1]), + 'SQL_ASCII'),'\x00') + from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r; + $f$; + + -- end + + |Build Status| |PyPi Version| |Coverage| .. |Build Status| image:: https://travis-ci.org/Pegase745/sqlalchemy-datatables.svg?branch=master diff --git a/datatables/__init__.py b/datatables/__init__.py index 234fdfe..1014a9c 100644 --- a/datatables/__init__.py +++ b/datatables/__init__.py @@ -143,7 +143,7 @@ def yadcf_multi_select(expr, value): ColumnTuple = namedtuple( 'ColumnDT', ['sqla_expr', 'column_name', 'mData', 'search_method', - 'nulls_order', 'global_search']) + 'nulls_order', 'global_search', 'sort_method']) class InvalidParameter(Exception): @@ -178,7 +178,7 @@ class ColumnDT(ColumnTuple): def __new__(cls, sqla_expr, column_name=None, mData=None, search_method='string_contains', nulls_order=None, - global_search=True): + global_search=True, sort_method=None): """Set default values for mData and filter. On creation, sets default None values for mData and string value for @@ -194,7 +194,7 @@ def __new__(cls, sqla_expr, column_name=None, mData=None, return super(ColumnDT, cls).__new__( cls, sqla_expr, column_name, mData, search_method, - nulls_order, global_search) + nulls_order, global_search, sort_method) class DataTables: @@ -374,6 +374,8 @@ def _set_sort_expressions(self): column = self.columns[column_nr] direction = self.params.get('order[{:d}][dir]'.format(i)) sort_expr = column.sqla_expr + if column.sort_method is not None: + sort_expr = column.sort_method if direction == 'asc': sort_expr = sort_expr.asc() elif direction == 'desc': diff --git a/examples/flask_tut/flask_tut/__init__.py b/examples/flask_tut/flask_tut/__init__.py index 50f21e2..ccd1a17 100644 --- a/examples/flask_tut/flask_tut/__init__.py +++ b/examples/flask_tut/flask_tut/__init__.py @@ -1,6 +1,7 @@ """Flask tutorial views.""" from flask import render_template, request, jsonify from flask import Flask +from sqlalchemy import func from datatables import ColumnDT, DataTables @@ -33,7 +34,7 @@ def data(): # defining columns columns = [ ColumnDT(User.id), - ColumnDT(User.name), + ColumnDT(User.name, sort_method=func.public.naturalsort(User.name)), ColumnDT(Address.description), ColumnDT(User.created_at) ] diff --git a/examples/pyramid_tut/pyramid_tut/views.py b/examples/pyramid_tut/pyramid_tut/views.py index bc7130b..64217b9 100644 --- a/examples/pyramid_tut/pyramid_tut/views.py +++ b/examples/pyramid_tut/pyramid_tut/views.py @@ -62,7 +62,7 @@ def data(request): # in the table columns = [ ColumnDT(User.id), - ColumnDT(User.name), + ColumnDT(User.name, sort_method=func.public.naturalsort(User.name)), ColumnDT(Address.description), ColumnDT(func.strftime('%d-%m-%Y', User.birthday)), ColumnDT(User.age)