Improvements for books table editor
This commit is contained in:
parent
94b5ec91cc
commit
1c681ee378
|
@ -240,6 +240,9 @@ class Languages(Base):
|
||||||
self.lang_code = lang_code
|
self.lang_code = lang_code
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
|
if self.language_name:
|
||||||
|
return self.language_name
|
||||||
|
else:
|
||||||
return self.lang_code
|
return self.lang_code
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -27,6 +27,7 @@ import json
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from babel import Locale as LC
|
||||||
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
|
@ -902,16 +903,17 @@ def edit_list_book(param):
|
||||||
elif param =='series':
|
elif param =='series':
|
||||||
edit_book_series(vals['value'], book)
|
edit_book_series(vals['value'], book)
|
||||||
elif param =='publishers':
|
elif param =='publishers':
|
||||||
edit_book_publisher(vals['value'], book)
|
vals['publisher'] = vals['value']
|
||||||
# ToDo: edit books
|
edit_book_publisher(vals, book)
|
||||||
elif param =='languages':
|
elif param =='languages':
|
||||||
edit_book_languages(vals['value'], book)
|
edit_book_languages(vals['value'], book)
|
||||||
elif param =='title':
|
|
||||||
edit_book_languages(vals['value'], book)
|
|
||||||
elif param =='sort':
|
|
||||||
edit_book_languages(vals['value'], book)
|
|
||||||
elif param =='author_sort':
|
elif param =='author_sort':
|
||||||
edit_book_languages(vals['value'], book)
|
book.author_sort = vals['value']
|
||||||
|
elif param =='title':
|
||||||
|
book.title = vals['value']
|
||||||
|
elif param =='sort':
|
||||||
|
book.sort = vals['value']
|
||||||
|
# ToDo: edit books
|
||||||
elif param =='authors':
|
elif param =='authors':
|
||||||
edit_book_languages(vals['value'], book)
|
edit_book_languages(vals['value'], book)
|
||||||
|
|
||||||
|
@ -919,7 +921,18 @@ def edit_list_book(param):
|
||||||
calibre_db.session.commit()
|
calibre_db.session.commit()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@editbook.route("/ajax/sort_value")
|
||||||
|
@login_required
|
||||||
|
def get_sorted_entry():
|
||||||
|
pass
|
||||||
|
|
||||||
@editbook.route("/ajax/deletebooks")
|
@editbook.route("/ajax/deletebooks")
|
||||||
@login_required_if_no_ano
|
@login_required
|
||||||
def delete_list_book():
|
def delete_list_book():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@editbook.route("/ajax/mergebooks", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def merge_list_book():
|
||||||
|
vals = request.get_json()
|
||||||
|
return ""
|
||||||
|
|
|
@ -51,9 +51,9 @@ body h2 {
|
||||||
color:#444;
|
color:#444;
|
||||||
}
|
}
|
||||||
|
|
||||||
a, .danger, .editable-empty, .editable-empty:hover { color: #45b29d; }
|
a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; }
|
||||||
|
|
||||||
.danger:hover { color: #23527c; }
|
.book-remove:hover { color: #23527c; }
|
||||||
|
|
||||||
.btn-default a { color: #444; }
|
.btn-default a { color: #444; }
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,150 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* exported TableActions, RestrictionActions, EbookActions*/
|
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
|
||||||
|
|
||||||
|
var selections = [];
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
$("#books-table").bootstrapTable({
|
$("#books-table").on("check.bs.table check-all.bs.table uncheck.bs.table uncheck-all.bs.table",
|
||||||
formatNoMatches: function () {
|
function (e, rowsAfter, rowsBefore) {
|
||||||
return "";
|
var rows = rowsAfter;
|
||||||
|
|
||||||
|
if (e.type === "uncheck-all") {
|
||||||
|
rows = rowsBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids = $.map(!$.isArray(rows) ? [rows] : rows, function (row) {
|
||||||
|
return row.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
var func = $.inArray(e.type, ["check", "check-all"]) > -1 ? "union" : "difference";
|
||||||
|
selections = window._[func](selections, ids);
|
||||||
|
if (selections.length >= 2) {
|
||||||
|
$("#merge_books").removeClass("disabled");
|
||||||
|
$("#merge_books").attr("aria-disabled", false);
|
||||||
|
} else {
|
||||||
|
$("#merge_books").addClass("disabled");
|
||||||
|
$("#merge_books").attr("aria-disabled", true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#merge_books").click(function() {
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/mergebooks",
|
||||||
|
data: JSON.stringify({"Merge_books":selections}),
|
||||||
|
success: function success() {
|
||||||
|
// ToDo:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
/*$("#books-table").bootstrapTable({
|
||||||
|
});
|
||||||
|
test = $("#books-table").bootstrapTable('getOptions');
|
||||||
|
var column = test.columns[0]
|
||||||
|
column.forEach(function(item){
|
||||||
|
if ('editableValidate' in item) {
|
||||||
|
item.editable = {
|
||||||
|
mode: 'inline',
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#books-table").bootstrapTable('refreshOptions', {columns: [column]});*/
|
||||||
|
|
||||||
|
var column = [];
|
||||||
|
$('#table1 > thead > tr > th').each(function(){
|
||||||
|
if ($(this).attr('data-edit')) {
|
||||||
|
if ($(this).attr('data-edit-validate')) {
|
||||||
|
column.push({
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
column.push({
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
column.push({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#table1").bootstrapTable({
|
||||||
|
sidePagination: "server",
|
||||||
|
pagination: true,
|
||||||
|
paginationDetailHAlign: " hidden",
|
||||||
|
paginationHAlign: "left",
|
||||||
|
/*url: window.location.pathname + "/../../ajax/listbooks",*/
|
||||||
|
idField: "id",
|
||||||
|
search: true,
|
||||||
|
showColumns: true,
|
||||||
|
searchAlign: "left",
|
||||||
|
showSearchButton : false,
|
||||||
|
searchOnEnterKey: true,
|
||||||
|
checkboxHeader: false,
|
||||||
|
maintainMetaData: true,
|
||||||
|
responseHandler: responseHandler,
|
||||||
|
columns: column,
|
||||||
|
/*editable-mode="inline"
|
||||||
|
editable-emptytext="<span class='glyphicon glyphicon-plus'></span>">*/
|
||||||
|
/*columns: [
|
||||||
|
{},
|
||||||
|
{}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
editable: {
|
||||||
|
mode: 'inline',
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
|
||||||
|
},
|
||||||
|
}],*/
|
||||||
|
formatNoMatches: function () {
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/*var $table = $('#books-table');
|
||||||
|
$('#books-table').bootstrapTable('getOptions')*/
|
||||||
|
/*$('#books-table').bootstrapTable('editable')*/
|
||||||
|
/*{ field: 'aimValue', title: 'Aim<br>Value', class: 'danger',editable: { mode: 'inline', validate: function (v) { return validateData(this, v); }, }, },*/
|
||||||
|
|
||||||
|
|
||||||
$("#domain_allow_submit").click(function(event) {
|
$("#domain_allow_submit").click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$("#domain_add_allow").ajaxForm();
|
$("#domain_add_allow").ajaxForm();
|
||||||
|
@ -105,7 +239,7 @@ $(function() {
|
||||||
var deleteId = $(this).data("deleteid");
|
var deleteId = $(this).data("deleteid");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:"get",
|
method:"get",
|
||||||
url: window.location.pathname + "/../../delete"/+deleteId,
|
url: window.location.pathname + "/../../delete/" + deleteId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -238,6 +372,7 @@ function TableActions (value, row) {
|
||||||
].join("");
|
].join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Function for deleting domain restrictions */
|
/* Function for deleting domain restrictions */
|
||||||
function RestrictionActions (value, row) {
|
function RestrictionActions (value, row) {
|
||||||
return [
|
return [
|
||||||
|
@ -250,8 +385,22 @@ function RestrictionActions (value, row) {
|
||||||
/* Function for deleting books */
|
/* Function for deleting books */
|
||||||
function EbookActions (value, row) {
|
function EbookActions (value, row) {
|
||||||
return [
|
return [
|
||||||
"<div class=\"danger remove\" data-toggle=\"modal\" data-target=\"#deleteModal\" data-delete-id=\"" + row.id + "\" title=\"Remove\">",
|
"<div class=\"book-remove\" data-toggle=\"modal\" data-target=\"#deleteModal\" data-delete-id=\"" + row.id + "\" title=\"Remove\">",
|
||||||
"<i class=\"glyphicon glyphicon-trash\"></i>",
|
"<i class=\"glyphicon glyphicon-trash\"></i>",
|
||||||
"</div>"
|
"</div>"
|
||||||
].join("");
|
].join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Function for keeping checked rows */
|
||||||
|
function responseHandler(res) {
|
||||||
|
$.each(res.rows, function (i, row) {
|
||||||
|
row.state = $.inArray(row.id, selections) !== -1;
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validato(value) {
|
||||||
|
if($.trim(value) == '') return 'This field is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*function validateVal(value) { if($.trim(value) == '') return 'This field is required'; }*/
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% macro text_table_row(parameter, edit_text, show_text) -%}
|
{% macro text_table_row(parameter, edit_text, show_text, validate) -%}
|
||||||
<th data-field="{{ parameter }}" id="{{ parameter }}" data-sortable="true" {% if g.user.role_edit() %}data-editable-type="text" data-editable-url="{{ url_for('editbook.edit_list_book', param=parameter)}}" data-editable="true" data-editable-title="{{ edit_text}}"{% endif %}>{{ show_text }}</th>
|
<th data-field="{{ parameter }}" id="{{ parameter }}" data-sortable="true"
|
||||||
|
{% if g.user.role_edit() %}
|
||||||
|
data-editable-type="text"
|
||||||
|
data-editable-url="{{ url_for('editbook.edit_list_book', param=parameter)}}"
|
||||||
|
data-editable-title="{{ edit_text}}"
|
||||||
|
data-edit="true"
|
||||||
|
{% if validate %}data-edit-validate="true" {% endif %}
|
||||||
|
{% endif %}
|
||||||
|
>{{ show_text }}</th>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
|
@ -9,13 +17,14 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h2 class="{{page}}">{{_(title)}}</h2>
|
<h2 class="{{page}}">{{_(title)}}</h2>
|
||||||
<table class="table table-no-bordered table-striped"
|
<!--table id="table1"></table-->
|
||||||
|
<!--a href="{{url_for('editbook.merge_list_book')}}" class="btn btn-default disabled" id="merge_book" role="button" aria-disabled="true">{{_('Merge selected books')}}
|
||||||
data-side-pagination="server"
|
data-side-pagination="server"
|
||||||
data-pagination="true"
|
data-pagination="true"
|
||||||
data-pagination-detail-h-align=" hidden"
|
data-pagination-detail-h-align=" hidden"
|
||||||
data-pagination-h-align="left"
|
data-pagination-h-align="left"
|
||||||
id="books-table"
|
id="books-table"
|
||||||
data-url="{{url_for('web.list_books')}}"
|
|
||||||
data-id-field="id"
|
data-id-field="id"
|
||||||
data-editable-mode="inline"
|
data-editable-mode="inline"
|
||||||
data-show-columns="true"
|
data-show-columns="true"
|
||||||
|
@ -26,24 +35,27 @@
|
||||||
data-checkbox-header="false"
|
data-checkbox-header="false"
|
||||||
data-maintain-meta-data="true"
|
data-maintain-meta-data="true"
|
||||||
data-response-handler="responseHandler"
|
data-response-handler="responseHandler"
|
||||||
data-editable-emptytext="<span class='glyphicon glyphicon-plus'></span>">
|
data-editable-emptytext="<span class='glyphicon glyphicon-plus'></span>"
|
||||||
|
</a-->
|
||||||
|
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">{{_('Merge selected books')}}</div>
|
||||||
|
<table id="table1" class="table table-no-bordered table-striped"
|
||||||
|
data-url="{{url_for('web.list_books')}}">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{% if g.user.role_edit() %}
|
{% if g.user.role_edit() %}
|
||||||
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
||||||
{{ text_table_row('title', _('Enter Title'),_('Title')) }}
|
{{ text_table_row('title', _('Enter Title'),_('Title'), true) }}
|
||||||
{{ text_table_row('sort', _('Enter Titlesort'),_('Sort')) }}
|
{{ text_table_row('sort', _('Enter Titlesort'),_('Sort'), false) }}
|
||||||
{{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort')) }}
|
{{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort'), false) }}
|
||||||
{{ text_table_row('authors', _('Enter Authors'),_('Authors')) }}
|
{{ text_table_row('authors', _('Enter Authors'),_('Authors'), false) }}
|
||||||
{{ text_table_row('tags', _('Enter Tags'),_('Tags')) }}
|
{{ text_table_row('tags', _('Enter Tags'),_('Tags'), false) }}
|
||||||
{{ text_table_row('series', _('Enter Series'),_('Series')) }}
|
{{ text_table_row('series', _('Enter Series'),_('Series'), false) }}
|
||||||
<th data-field="series_index" id="series_index" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('editbook.edit_list_book', param='series_index')}}" data-editable="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th>
|
<th data-field="series_index" id="series_index" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('editbook.edit_list_book', param='series_index')}}" data-editable="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th>
|
||||||
{{ text_table_row('languages', _('Enter Languages'),_('Languages')) }}
|
{{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }}
|
||||||
<th data-field="pubdate" id="pubdate" data-sortable="true">Publishing Date</th>
|
<th data-field="pubdate" data-type="date" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">Publishing Date</th>
|
||||||
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers')) }}
|
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false) }}
|
||||||
{% if g.user.role_edit() %}
|
{% if g.user.role_edit() %}
|
||||||
<th data-align="right" data-formatter="EbookActions" data-switchable="false"></th>
|
<th data-align="right" data-formatter="EbookActions" data-switchable="false"></th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -60,32 +72,5 @@
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
var $table = $('#books-table')
|
|
||||||
var selections = []
|
|
||||||
|
|
||||||
function responseHandler(res) {
|
|
||||||
$.each(res.rows, function (i, row) {
|
|
||||||
row.state = $.inArray(row.id, selections) !== -1
|
|
||||||
})
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$table.on('check.bs.table check-all.bs.table uncheck.bs.table uncheck-all.bs.table',
|
|
||||||
function (e, rowsAfter, rowsBefore) {
|
|
||||||
var rows = rowsAfter
|
|
||||||
|
|
||||||
if (e.type === 'uncheck-all') {
|
|
||||||
rows = rowsBefore
|
|
||||||
}
|
|
||||||
|
|
||||||
var ids = $.map(!$.isArray(rows) ? [rows] : rows, function (row) {
|
|
||||||
return row.id
|
|
||||||
})
|
|
||||||
|
|
||||||
var func = $.inArray(e.type, ['check', 'check-all']) > -1 ? 'union' : 'difference'
|
|
||||||
selections = window._[func](selections, ids)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
10
cps/web.py
10
cps/web.py
|
@ -30,8 +30,8 @@ import traceback
|
||||||
import binascii
|
import binascii
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from babel import Locale as LC
|
|
||||||
from babel.dates import format_date
|
from babel.dates import format_date
|
||||||
|
from babel import Locale as LC
|
||||||
from babel.core import UnknownLocaleError
|
from babel.core import UnknownLocaleError
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for
|
from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for
|
||||||
|
@ -849,6 +849,14 @@ def list_books():
|
||||||
else:
|
else:
|
||||||
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order)
|
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order)
|
||||||
filtered_count = total_count
|
filtered_count = total_count
|
||||||
|
for entry in entries:
|
||||||
|
for index in range(0, len(entry.languages)):
|
||||||
|
try:
|
||||||
|
entry.languages[index].language_name = LC.parse(entry.languages[index].lang_code)\
|
||||||
|
.get_language_name(get_locale())
|
||||||
|
except UnknownLocaleError:
|
||||||
|
entry.languages[index].language_name = _(
|
||||||
|
isoLanguages.get(part3=entry.languages[index].lang_code).name)
|
||||||
table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries}
|
table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries}
|
||||||
js_list = json.dumps(table_entries, cls=db.AlchemyEncoder)
|
js_list = json.dumps(table_entries, cls=db.AlchemyEncoder)
|
||||||
#js_list = json.dumps(entries, cls=db.AlchemyEncoder)
|
#js_list = json.dumps(entries, cls=db.AlchemyEncoder)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user