Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
cd3a3117
Commit
cd3a3117
authored
Dec 15, 2013
by
Bryan Hoyt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Better factoring of named URL filters, to allow user-customisable filter names
parent
8a9f3c6b
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
73 additions
and
81 deletions
+73
-81
list.html
examples/layout/templates/list.html
+1
-1
base.py
flask_admin/model/base.py
+45
-38
filters.py
flask_admin/model/filters.py
+0
-8
filters.js
flask_admin/static/admin/js/filters.js
+14
-17
layout.html
flask_admin/templates/admin/model/layout.html
+11
-13
list.html
flask_admin/templates/admin/model/list.html
+2
-4
No files found.
examples/layout/templates/list.html
View file @
cd3a3117
flask_admin/model/base.py
View file @
cd3a3117
...
...
@@ -252,6 +252,15 @@ class BaseModelView(BaseView, ActionsMixin):
column_filters = ('user', 'email')
"""
named_filter_urls
=
False
"""
Set to True to use human-readable names for filters in URL parameters.
False by default so as to be robust across translations.
If you override unique_filter_label(), this has no effect.
"""
column_display_pk
=
ObsoleteAttr
(
'column_display_pk'
,
'list_display_pk'
,
False
)
...
...
@@ -544,25 +553,19 @@ class BaseModelView(BaseView, ActionsMixin):
self
.
column_descriptions
=
dict
()
if
self
.
_filters
:
self
.
_filter_groups
=
[]
self
.
_filter_dict
=
dict
()
for
i
,
n
in
enumerate
(
self
.
_filters
):
if
n
.
name
not
in
self
.
_filter_dict
:
group
=
[]
self
.
_filter_dict
[
n
.
name
]
=
group
self
.
_filter_groups
.
append
((
n
.
name
,
group
))
self
.
_flattened_filters_by_group
=
{}
for
flt
in
self
.
_filters
:
if
flt
.
name
not
in
self
.
_flattened_filters_by_group
:
self
.
_flattened_filters_by_group
[
flt
.
name
]
=
[]
group
=
self
.
_flattened_filters_by_group
[
flt
.
name
]
group
.
append
({
'name'
:
flt
.
name
,
'label'
:
self
.
unique_filter_label
(
flt
),
'operation'
:
flt
.
operation
(),
'options'
:
flt
.
get_options
(
self
)
or
None
,
'data_type'
:
flt
.
data_type
})
else
:
group
=
self
.
_filter_dict
[
n
.
name
]
group
.
append
((
i
,
n
.
operation
()))
self
.
_filter_types
=
dict
((
i
,
f
.
data_type
)
for
i
,
f
in
enumerate
(
self
.
_filters
)
if
f
.
data_type
)
else
:
self
.
_filter_groups
=
None
self
.
_filter_types
=
None
self
.
_flattened_filters_by_group
=
None
# Form rendering rules
if
self
.
form_create_rules
:
...
...
@@ -948,6 +951,24 @@ class BaseModelView(BaseView, ActionsMixin):
def
get_empty_list_message
(
self
):
return
gettext
(
'There are no items in the table.'
)
def
unique_filter_label
(
self
,
flt
):
"""
Given a filter `flt`, return a unique name for that filter in
this view.
By default, returns a numeric index or a human-readable filter name
Does not include the `flt[n]_` portion of the filter name.
To use custom names, override this function, eg
`def unique_filter_label(self, flt): return flt.__class__.__name__`
"""
if
self
.
named_filter_urls
:
return
u'{name}_{operation}'
.
format
(
name
=
flt
.
name
,
operation
=
flt
.
operation
())
.
lower
()
.
replace
(
' '
,
'_'
)
else
:
return
unicode
(
self
.
_filters
.
index
(
flt
))
def
get_filter_args
(
self
):
"""
Retrieve and parse filter parameters from the request URL.
...
...
@@ -960,7 +981,7 @@ class BaseModelView(BaseView, ActionsMixin):
if
not
self
.
_filters
:
return
None
filter_idx_by_label
=
dict
((
flt
.
query_label
(
),
i
)
for
i
,
flt
in
enumerate
(
self
.
_filters
))
filter_idx_by_label
=
dict
((
self
.
unique_filter_label
(
flt
),
i
)
for
i
,
flt
in
enumerate
(
self
.
_filters
))
sfilters
=
[]
...
...
@@ -1033,7 +1054,7 @@ class BaseModelView(BaseView, ActionsMixin):
if
filters
:
for
flt
in
filters
:
key
=
'flt_
%
s'
%
self
.
_filters
[
flt
[
0
]]
.
query_label
(
)
key
=
'flt_
%
s'
%
self
.
unique_filter_label
(
self
.
_filters
[
flt
[
0
]]
)
kwargs
[
key
]
=
flt
[
1
]
return
url_for
(
view
,
**
kwargs
)
...
...
@@ -1054,11 +1075,11 @@ class BaseModelView(BaseView, ActionsMixin):
"""
return
rec_getattr
(
model
,
name
)
def
_get_filter_dict
(
self
):
def
filters_by_label
(
self
):
"""
Return flattened filter dictionary which can be JSON-serialized
.
Flattened dict of all filters, indexed by their label
.
"""
return
dict
((
as_unicode
(
k
),
v
)
for
k
,
v
in
iteritems
(
self
.
_filter_dict
)
)
return
dict
((
self
.
unique_filter_label
(
flt
),
flt
)
for
flt
in
self
.
_filters
)
@
contextfunction
def
get_list_value
(
self
,
context
,
model
,
name
):
...
...
@@ -1137,18 +1158,6 @@ class BaseModelView(BaseView, ActionsMixin):
if
count
%
self
.
page_size
!=
0
:
num_pages
+=
1
# Pregenerate filters
if
self
.
_filters
:
filters_data
=
dict
()
for
idx
,
f
in
enumerate
(
self
.
_filters
):
flt_data
=
f
.
get_options
(
self
)
if
flt_data
:
filters_data
[
idx
]
=
flt_data
else
:
filters_data
=
None
# Various URL generation helpers
def
pager_url
(
p
):
# Do not add page number if it is first page
...
...
@@ -1203,9 +1212,7 @@ class BaseModelView(BaseView, ActionsMixin):
search
=
search
,
# Filters
filters
=
self
.
_filters
,
filter_groups
=
self
.
_filter_groups
,
filter_types
=
self
.
_filter_types
,
filter_data
=
filters_data
,
filter_groups
=
self
.
_flattened_filters_by_group
,
active_filters
=
filters
,
# Actions
...
...
flask_admin/model/filters.py
View file @
cd3a3117
...
...
@@ -72,14 +72,6 @@ class BaseFilter(object):
"""
raise
NotImplemented
()
def
query_label
(
self
):
"""
Return a string that can be used in a url to identify this filter.
For example u'username_equals'
"""
return
'{name}_{operation}'
.
format
(
name
=
self
.
name
,
operation
=
self
.
operation
())
.
lower
()
.
replace
(
' '
,
'_'
)
def
__unicode__
(
self
):
return
self
.
name
...
...
flask_admin/static/admin/js/filters.js
View file @
cd3a3117
var
AdminFilters
=
function
(
element
,
filters_element
,
operations
,
options
,
types
)
{
var
AdminFilters
=
function
(
element
,
filters_element
,
filters_by_group
)
{
var
$root
=
$
(
element
);
var
$container
=
$
(
'.filters'
,
$root
);
var
lastCount
=
0
;
...
...
@@ -23,7 +23,7 @@ var AdminFilters = function(element, filters_element, operations, options, types
return
false
;
}
function
addFilter
(
name
,
op
)
{
function
addFilter
(
name
,
subfilters
)
{
var
$el
=
$
(
'<tr />'
).
appendTo
(
$container
);
// Filter list
...
...
@@ -41,9 +41,9 @@ var AdminFilters = function(element, filters_element, operations, options, types
var
$select
=
$
(
'<select class="filter-op" />'
)
.
change
(
changeOperation
);
$
(
op
).
each
(
function
()
{
var
filter_label
=
(
name
+
'_'
+
this
[
1
]).
toLowerCase
().
replace
(
/ /g
,
'_'
);
$select
.
append
(
$
(
'<option/>'
).
attr
(
'value'
,
filter_label
).
text
(
this
[
1
]
));
$
(
subfilters
).
each
(
function
()
{
console
.
log
(
this
);
$select
.
append
(
$
(
'<option/>'
).
attr
(
'value'
,
this
.
label
).
text
(
this
.
operation
));
});
$el
.
append
(
...
...
@@ -52,17 +52,14 @@ var AdminFilters = function(element, filters_element, operations, options, types
$select
.
select2
({
width
:
'resolve'
});
// Input
var
optId
=
op
[
0
][
0
];
var
filter_label
=
(
name
+
'_'
+
op
[
0
][
1
]).
toLowerCase
().
replace
(
/ /g
,
'_'
);
var
$field
;
if
(
optId
in
options
)
{
var
firstFilter
=
subfilters
[
0
];
if
(
firstFilter
.
options
)
{
$field
=
$
(
'<select class="filter-val" />'
)
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
fi
lter_
label
);
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
fi
rstFilter
.
label
);
$
(
options
[
optId
]
).
each
(
function
()
{
$
(
firstFilter
.
options
).
each
(
function
()
{
$field
.
append
(
$
(
'<option/>'
)
.
val
(
this
[
0
]).
text
(
this
[
1
]));
});
...
...
@@ -72,13 +69,13 @@ var AdminFilters = function(element, filters_element, operations, options, types
}
else
{
$field
=
$
(
'<input type="text" class="filter-val" />'
)
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
fi
lter_
label
);
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
fi
rstFilter
.
label
);
$el
.
append
(
$
(
'<td/>'
).
append
(
$field
));
}
if
(
optId
in
types
)
{
$field
.
attr
(
'data-role'
,
types
[
optId
]
);
faForm
.
applyStyle
(
$field
,
types
[
optId
]
);
if
(
firstFilter
.
data_type
)
{
$field
.
attr
(
'data-role'
,
firstFilter
.
data_type
);
faForm
.
applyStyle
(
$field
,
firstFilter
.
data_type
);
}
lastCount
+=
1
;
...
...
@@ -86,7 +83,7 @@ var AdminFilters = function(element, filters_element, operations, options, types
$
(
'a.filter'
,
filters_element
).
click
(
function
()
{
var
name
=
$
(
this
).
text
().
trim
();
addFilter
(
name
,
operations
[
name
]);
addFilter
(
name
,
filters_by_group
[
name
]);
$
(
'button'
,
$root
).
show
();
...
...
flask_admin/templates/admin/model/layout.html
View file @
cd3a3117
...
...
@@ -5,7 +5,7 @@
<ul
class=
"dropdown-menu field-filters"
>
{% for k in filter_groups %}
<li>
<a
href=
"javascript:void(0)"
class=
"filter"
onclick=
"return false;"
>
{{ k
[0]
}}
</a>
<a
href=
"javascript:void(0)"
class=
"filter"
onclick=
"return false;"
>
{{ k }}
</a>
</li>
{% endfor %}
</ul>
...
...
@@ -23,31 +23,29 @@
<table
class=
"filters"
>
{%- for i, flt in enumerate(active_filters) -%}
<tr>
{% set filter = admin_view._filters[flt[0]] %}
{% set filter = filters[flt[0]] %}
{%- set filter_label = admin_view.unique_filter_label(filter) -%}
<td>
<a
href=
"javascript:void(0)"
class=
"btn remove-filter"
title=
"{{ _gettext('Remove Filter') }}"
>
<span
class=
"close-icon"
>
×
</span>
{{ filter
s[flt[0]]
}}
<span
class=
"close-icon"
>
×
</span>
{{ filter
.name
}}
</a>
</td>
<td>
<select
class=
"filter-op"
data-role=
"select2"
>
{% for op in admin_view._filter_dict[filter.name] %}
{{ op }}
{%- set filter_label = filter.name.lower() + '_' + op[1].replace(' ', '_') -%}
<option
value=
"{{ filter_label }}"
{%
if
flt
[
0
]
==
op
[
0
]
%}
selected=
"selected"
{%
endif
%}
>
{{ op[1] }}
</option>
{% for subfilter in filter_groups[filter.name] %}
<option
value=
"{{ subfilter.label }}"
{%
if
subfilter
.
label =
=
filter_label
%}
selected=
"selected"
{%
endif
%}
>
{{ subfilter.operation }}
</option>
{% endfor %}
</select>
</td>
<td>
{%- set data = filter_data.get(flt[0]) -%}
{%- if data -%}
<select
name=
"flt{{ i }}_{{ filter.query_label() }}"
class=
"filter-val"
data-role=
"select2"
>
{%- for d in data %}
<option
value=
"{{ d[0] }}"
{%
if
flt
[
1
]
==
d
[
0
]
%}
selected
{%
endif
%}
>
{{ d[1] }}
</option>
{%- if filter.options -%}
<select
name=
"flt{{ i }}_{{ filter_label }}"
class=
"filter-val"
data-role=
"select2"
>
{%- for o in filter.options %}
<option
value=
"{{ o[0] }}"
{%
if
flt
[
1
]
==
o
[
0
]
%}
selected
{%
endif
%}
>
{{ o[1] }}
</option>
{%- endfor %}
</select>
{%- else -%}
<input
name=
"flt{{ i }}_{{ filter
.query_label() }}"
type=
"text"
value=
"{{ flt[1] or '' }}"
class=
"filter-val"
{%
if
flt
[
0
]
in
filter_types
%}
data-role=
"{{ filter_types[flt[0]]
}}"
{%
endif
%}
></input>
<input
name=
"flt{{ i }}_{{ filter
_label }}"
type=
"text"
value=
"{{ flt[1] or '' }}"
class=
"filter-val"
{%
if
filter
.
data_type
%}
data-role=
"{{ filter.data_type
}}"
{%
endif
%}
></input>
{%- endif -%}
</td>
</tr>
...
...
flask_admin/templates/admin/model/list.html
View file @
cd3a3117
...
...
@@ -157,12 +157,10 @@
html
:
true
,
placement
:
'bottom'
});
{
%
if
filter_groups
is
not
none
and
filter_data
is
not
none
%
}
{
%
if
filter_groups
is
not
none
%
}
var
filter
=
new
AdminFilters
(
'#filter_form'
,
'.field-filters'
,
{{
admin_view
.
_get_filter_dict
()
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_types
|
tojson
|
safe
}}
{{
filter_groups
|
tojson
|
safe
}}
);
{
%
endif
%
}
})(
jQuery
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment