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
73ed6524
Commit
73ed6524
authored
Mar 27, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Started working on the filters.
parent
0edfb4a9
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
609 additions
and
64 deletions
+609
-64
TODO.txt
TODO.txt
+3
-2
simple.py
examples/sqla/simple.py
+4
-1
__init__.py
flask_adminex/ext/sqlamodel/__init__.py
+1
-0
filters.py
flask_adminex/ext/sqlamodel/filters.py
+102
-0
tools.py
flask_adminex/ext/sqlamodel/tools.py
+9
-0
view.py
flask_adminex/ext/sqlamodel/view.py
+73
-28
__init__.py
flask_adminex/model/__init__.py
+1
-0
base.py
flask_adminex/model/base.py
+150
-30
filters.py
flask_adminex/model/filters.py
+70
-0
admin.css
flask_adminex/static/css/admin.css
+17
-1
filters.js
flask_adminex/static/js/filters.js
+108
-0
form.js
flask_adminex/static/js/form.js
+20
-2
list.html
flask_adminex/templates/admin/model/list.html
+51
-0
No files found.
TODO.txt
View file @
73ed6524
...
@@ -3,9 +3,9 @@
...
@@ -3,9 +3,9 @@
- Calendar - add validation for time without seconds (automatically add seconds)
- Calendar - add validation for time without seconds (automatically add seconds)
- Model Admin
- Model Admin
- Ability to sort by fields that are not visible?
- Ability to sort by fields that are not visible?
- Exclude for list columns
- Exclude for form fields
- List display callables
- List display callables
- Search
- Rename init_search
- Built-in filtering support
- Built-in filtering support
- Configurable operations (=, >, <, etc)
- Configurable operations (=, >, <, etc)
- Callable operations
- Callable operations
...
@@ -20,5 +20,6 @@
...
@@ -20,5 +20,6 @@
- Header title
- Header title
- Mass-delete functionality
- Mass-delete functionality
- File size restriction
- File size restriction
- Localization
- Unit tests
- Unit tests
- Documentation
- Documentation
examples/sqla/simple.py
View file @
73ed6524
...
@@ -3,6 +3,7 @@ from flaskext.sqlalchemy import SQLAlchemy
...
@@ -3,6 +3,7 @@ from flaskext.sqlalchemy import SQLAlchemy
from
flask.ext
import
adminex
,
wtf
from
flask.ext
import
adminex
,
wtf
from
flask.ext.adminex.ext
import
sqlamodel
from
flask.ext.adminex.ext
import
sqlamodel
from
flask.ext.adminex.ext.sqlamodel
import
filters
# Create application
# Create application
app
=
Flask
(
__name__
)
app
=
Flask
(
__name__
)
...
@@ -61,6 +62,8 @@ class PostAdmin(sqlamodel.ModelView):
...
@@ -61,6 +62,8 @@ class PostAdmin(sqlamodel.ModelView):
searchable_columns
=
(
'title'
,
User
.
username
)
searchable_columns
=
(
'title'
,
User
.
username
)
column_filters
=
(
User
.
username
,
'title'
,
'date'
,
filters
.
FilterLike
(
Post
.
title
,
'Fixed Title'
,
options
=
((
'test1'
,
'Test 1'
),
(
'test2'
,
'Test 2'
))))
# Pass arguments to WTForms. In this case, change label for text field to
# Pass arguments to WTForms. In this case, change label for text field to
# be 'Big Text' and add required() validator.
# be 'Big Text' and add required() validator.
form_args
=
dict
(
form_args
=
dict
(
...
@@ -84,4 +87,4 @@ if __name__ == '__main__':
...
@@ -84,4 +87,4 @@ if __name__ == '__main__':
# Start app
# Start app
app
.
debug
=
True
app
.
debug
=
True
app
.
run
()
app
.
run
(
'0.0.0.0'
,
8000
)
flask_adminex/ext/sqlamodel/__init__.py
0 → 100644
View file @
73ed6524
from
.view
import
ModelView
flask_adminex/ext/sqlamodel/filters.py
0 → 100644
View file @
73ed6524
from
flask.ext.adminex.model
import
filters
from
flask.ext.adminex.ext.sqlamodel
import
tools
class
BaseSQLAFilter
(
filters
.
BaseFilter
):
def
__init__
(
self
,
column
,
name
,
options
=
None
,
data_type
=
None
):
super
(
BaseSQLAFilter
,
self
)
.
__init__
(
name
,
options
,
data_type
)
self
.
column
=
column
# Common filters
class
FilterEqual
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
==
value
)
def
__unicode__
(
self
):
return
'
%
s equals'
%
self
.
name
class
FilterNotEqual
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
!=
value
)
def
__unicode__
(
self
):
return
'
%
s not equal'
%
self
.
name
class
FilterLike
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
stmt
=
tools
.
parse_like_term
(
value
)
return
query
.
filter
(
self
.
column
.
ilike
(
stmt
))
def
__unicode__
(
self
):
return
'
%
s like'
%
self
.
name
class
FilterNotLike
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
stmt
=
tools
.
parse_like_term
(
value
)
return
query
.
filter
(
~
self
.
column
.
ilike
(
stmt
))
def
__unicode__
(
self
):
return
'
%
s not like'
%
self
.
name
class
FilterGreater
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
>
value
)
def
__unicode__
(
self
):
return
'
%
s greater than'
%
self
.
name
class
FilterSmaller
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
<
value
)
def
__unicode__
(
self
):
return
'
%
s smaller than'
%
self
.
name
# Customized type filters
class
BooleanEqualFilter
(
FilterEqual
,
filters
.
BaseBooleanFilter
):
pass
class
BooleanNotEqualFilter
(
FilterNotEqual
,
filters
.
BaseBooleanFilter
):
pass
# Base SQLA filter field converter
class
FilterConverter
(
filters
.
BaseFilterConverter
):
strings
=
(
FilterEqual
,
FilterNotEqual
,
FilterLike
,
FilterNotLike
)
numeric
=
(
FilterEqual
,
FilterNotEqual
,
FilterGreater
,
FilterSmaller
)
def
convert
(
self
,
type_name
,
column
,
name
):
if
type_name
in
self
.
converters
:
return
self
.
converters
[
type_name
](
column
,
name
)
return
None
@
filters
.
convert
(
'String'
,
'Unicode'
,
'Text'
,
'UnicodeText'
)
def
conv_string
(
self
,
column
,
name
):
return
[
f
(
column
,
name
)
for
f
in
self
.
strings
]
@
filters
.
convert
(
'Boolean'
)
def
conv_bool
(
self
,
column
,
name
):
return
[
BooleanEqualFilter
(
column
,
name
),
BooleanNotEqualFilter
(
column
,
name
)]
@
filters
.
convert
(
'Integer'
,
'SmallInteger'
,
'Numeric'
,
'Float'
)
def
conv_int
(
self
,
column
,
name
):
return
[
f
(
column
,
name
)
for
f
in
self
.
numeric
]
@
filters
.
convert
(
'Date'
)
def
conv_date
(
self
,
column
,
name
):
return
[
f
(
column
,
name
,
data_type
=
'datepicker'
)
for
f
in
self
.
numeric
]
@
filters
.
convert
(
'DateTime'
)
def
conv_datetime
(
self
,
column
,
name
):
return
[
f
(
column
,
name
,
data_type
=
'datetimepicker'
)
for
f
in
self
.
numeric
]
flask_adminex/ext/sqlamodel/tools.py
0 → 100644
View file @
73ed6524
def
parse_like_term
(
term
):
if
term
.
startswith
(
'^'
):
stmt
=
'
%
s
%%
'
%
term
[
1
:]
elif
term
.
startswith
(
'='
):
stmt
=
term
[
1
:]
else
:
stmt
=
'
%%%
s
%%
'
%
term
return
stmt
flask_adminex/ext/sqlamodel.py
→
flask_adminex/ext/sqlamodel
/view
.py
View file @
73ed6524
...
@@ -12,6 +12,7 @@ from flask import flash
...
@@ -12,6 +12,7 @@ from flask import flash
from
flask.ext.adminex
import
form
from
flask.ext.adminex
import
form
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex.ext.sqlamodel
import
filters
,
tools
class
Unique
(
object
):
class
Unique
(
object
):
...
@@ -220,6 +221,11 @@ class ModelView(BaseModelView):
...
@@ -220,6 +221,11 @@ class ModelView(BaseModelView):
For example, if you entered *=ZZZ*, *ILIKE 'ZZZ'* statement will be used.
For example, if you entered *=ZZZ*, *ILIKE 'ZZZ'* statement will be used.
"""
"""
filter_converter
=
filters
.
FilterConverter
()
"""
TBD:
"""
def
__init__
(
self
,
model
,
session
,
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
"""
"""
...
@@ -241,8 +247,9 @@ class ModelView(BaseModelView):
...
@@ -241,8 +247,9 @@ class ModelView(BaseModelView):
self
.
session
=
session
self
.
session
=
session
self
.
_search_fields
=
None
self
.
_search_fields
=
None
self
.
_search_joins
=
None
self
.
_search_joins_names
=
set
()
self
.
_search_joins_names
=
None
self
.
_filter_joins_names
=
set
()
super
(
ModelView
,
self
)
.
__init__
(
model
,
name
,
category
,
endpoint
,
url
)
super
(
ModelView
,
self
)
.
__init__
(
model
,
name
,
category
,
endpoint
,
url
)
...
@@ -312,6 +319,23 @@ class ModelView(BaseModelView):
...
@@ -312,6 +319,23 @@ class ModelView(BaseModelView):
return
columns
return
columns
def
_get_columns_for_field
(
self
,
field
):
if
isinstance
(
field
,
basestring
):
attr
=
getattr
(
self
.
model
,
field
,
None
)
if
field
is
None
:
raise
Exception
(
'Field
%
s was not found.'
%
field
)
else
:
attr
=
field
if
(
not
attr
or
not
hasattr
(
attr
,
'property'
)
or
not
hasattr
(
attr
.
property
,
'columns'
)
or
not
attr
.
property
.
columns
):
raise
Exception
(
'Invalid field
%
s: does not contains any columns.'
%
field
)
return
attr
.
property
.
columns
def
init_search
(
self
):
def
init_search
(
self
):
"""
"""
Initialize search. Returns `True` if search is supported for this
Initialize search. Returns `True` if search is supported for this
...
@@ -322,23 +346,10 @@ class ModelView(BaseModelView):
...
@@ -322,23 +346,10 @@ class ModelView(BaseModelView):
"""
"""
if
self
.
searchable_columns
:
if
self
.
searchable_columns
:
self
.
_search_fields
=
[]
self
.
_search_fields
=
[]
self
.
_search_joins
=
[]
self
.
_search_joins_names
=
set
()
self
.
_search_joins_names
=
set
()
for
p
in
self
.
searchable_columns
:
for
p
in
self
.
searchable_columns
:
# If item is a stirng, resolve it as an attribute
for
column
in
self
.
_get_columns_for_field
(
p
):
if
isinstance
(
p
,
basestring
):
attr
=
getattr
(
self
.
model
,
p
,
None
)
else
:
attr
=
p
# Only column searches are supported
if
(
not
attr
or
not
hasattr
(
attr
,
'property'
)
or
not
hasattr
(
attr
.
property
,
'columns'
)):
raise
Exception
(
'Invalid searchable column "
%
s"'
%
p
)
for
column
in
attr
.
property
.
columns
:
column_type
=
type
(
column
.
type
)
.
__name__
column_type
=
type
(
column
.
type
)
.
__name__
if
not
self
.
is_text_column_type
(
column_type
):
if
not
self
.
is_text_column_type
(
column_type
):
...
@@ -349,7 +360,6 @@ class ModelView(BaseModelView):
...
@@ -349,7 +360,6 @@ class ModelView(BaseModelView):
# If it belongs to different table - add a join
# If it belongs to different table - add a join
if
column
.
table
!=
self
.
model
.
__table__
:
if
column
.
table
!=
self
.
model
.
__table__
:
self
.
_search_joins
.
append
(
column
.
table
)
self
.
_search_joins_names
.
add
(
column
.
table
.
name
)
self
.
_search_joins_names
.
add
(
column
.
table
.
name
)
return
bool
(
self
.
searchable_columns
)
return
bool
(
self
.
searchable_columns
)
...
@@ -363,6 +373,31 @@ class ModelView(BaseModelView):
...
@@ -363,6 +373,31 @@ class ModelView(BaseModelView):
return
(
name
==
'String'
or
name
==
'Unicode'
or
return
(
name
==
'String'
or
name
==
'Unicode'
or
name
==
'Text'
or
name
==
'UnicodeText'
)
name
==
'Text'
or
name
==
'UnicodeText'
)
def
scaffold_filters
(
self
,
name
):
columns
=
self
.
_get_columns_for_field
(
name
)
if
len
(
columns
)
>
1
:
raise
Exception
(
'Can not filter more than on one column for
%
s'
%
name
)
column
=
columns
[
0
]
if
not
isinstance
(
name
,
basestring
):
visible_name
=
self
.
get_column_name
(
name
.
property
.
key
)
else
:
visible_name
=
self
.
get_column_name
(
name
)
type_name
=
type
(
column
.
type
)
.
__name__
flt
=
self
.
filter_converter
.
convert
(
type_name
,
column
,
visible_name
)
if
flt
:
# If there's relation to other table, do it
if
column
.
table
!=
self
.
model
.
__table__
:
self
.
_filter_joins_names
.
add
(
column
.
table
.
name
)
return
flt
def
scaffold_form
(
self
):
def
scaffold_form
(
self
):
"""
"""
Create form from the model.
Create form from the model.
...
@@ -395,7 +430,7 @@ class ModelView(BaseModelView):
...
@@ -395,7 +430,7 @@ class ModelView(BaseModelView):
return
joined
return
joined
# Database-related API
# Database-related API
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
,
search
,
execute
=
True
):
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
,
search
,
filters
,
execute
=
True
):
"""
"""
Return models from the database.
Return models from the database.
...
@@ -409,6 +444,8 @@ class ModelView(BaseModelView):
...
@@ -409,6 +444,8 @@ class ModelView(BaseModelView):
Search query
Search query
`execute`
`execute`
Execute query immediately? Default is `True`
Execute query immediately? Default is `True`
`filters`
List of filter tuples
"""
"""
# Will contain names of joined tables to avoid duplicate joins
# Will contain names of joined tables to avoid duplicate joins
...
@@ -416,11 +453,11 @@ class ModelView(BaseModelView):
...
@@ -416,11 +453,11 @@ class ModelView(BaseModelView):
query
=
self
.
session
.
query
(
self
.
model
)
query
=
self
.
session
.
query
(
self
.
model
)
# Apply search
before counting results
# Apply search
criteria
if
self
.
_search_supported
and
search
:
if
self
.
_search_supported
and
search
:
# Apply search-related joins
# Apply search-related joins
if
self
.
_search_joins
:
if
self
.
_search_joins
_names
:
query
=
query
.
join
(
*
self
.
_search_joins
)
query
=
query
.
join
(
*
self
.
_search_joins
_names
)
joins
|=
self
.
_search_joins_names
joins
|=
self
.
_search_joins_names
# Apply terms
# Apply terms
...
@@ -430,16 +467,24 @@ class ModelView(BaseModelView):
...
@@ -430,16 +467,24 @@ class ModelView(BaseModelView):
if
not
term
:
if
not
term
:
continue
continue
if
term
.
startswith
(
'^'
):
stmt
=
tools
.
parse_like_term
(
term
)
stmt
=
'
%
s
%%
'
%
term
[
1
:]
elif
term
.
startswith
(
'='
):
stmt
=
term
[
1
:]
else
:
stmt
=
'
%%%
s
%%
'
%
term
filter_stmt
=
[
c
.
ilike
(
stmt
)
for
c
in
self
.
_search_fields
]
filter_stmt
=
[
c
.
ilike
(
stmt
)
for
c
in
self
.
_search_fields
]
query
=
query
.
filter
(
or_
(
*
filter_stmt
))
query
=
query
.
filter
(
or_
(
*
filter_stmt
))
# Apply filters
if
self
.
_filters
:
# Apply search-related joins
if
self
.
_filter_joins_names
:
new_joins
=
self
.
_filter_joins_names
-
joins
if
new_joins
:
query
=
query
.
join
(
*
new_joins
)
joins
|=
self
.
_search_joins_names
# Apply filters
for
flt
,
value
in
filters
:
query
=
self
.
_filters
[
flt
]
.
apply
(
query
,
value
)
# Calculate number of rows
# Calculate number of rows
count
=
query
.
count
()
count
=
query
.
count
()
...
...
flask_adminex/model/__init__.py
0 → 100644
View file @
73ed6524
from
.base
import
BaseModelView
flask_adminex/model.py
→
flask_adminex/model
/base
.py
View file @
73ed6524
This diff is collapsed.
Click to expand it.
flask_adminex/model/filters.py
0 → 100644
View file @
73ed6524
class
BaseFilter
(
object
):
def
__init__
(
self
,
name
,
options
=
None
,
data_type
=
None
):
self
.
name
=
name
self
.
options
=
options
self
.
data_type
=
data_type
def
get_options
(
self
,
view
):
return
self
.
options
def
validate
(
self
,
value
):
return
True
def
apply
(
self
,
query
):
raise
NotImplemented
()
def
__unicode__
(
self
):
return
self
.
name
# Customized filters
class
BaseBooleanFilter
(
BaseFilter
):
def
__init__
(
self
,
name
,
data_type
=
None
):
super
(
BaseBooleanFilter
,
self
)
.
__init__
(
name
,
((
'1'
,
'Yes'
),
(
'0'
,
'No'
)),
data_type
)
def
validate
(
self
,
value
):
return
value
==
'0'
or
value
==
'1'
class
BaseDateFilter
(
BaseFilter
):
def
__init__
(
self
,
name
,
options
=
None
):
super
(
BaseDateFilter
,
self
)
.
__init__
(
name
,
options
,
data_type
=
'datepicker'
)
def
validate
(
self
,
value
):
# TODO: Validation
return
True
class
BaseDateTimeFilter
(
BaseFilter
):
def
__init__
(
self
,
name
,
options
=
None
):
super
(
BaseDateTimeFilter
,
self
)
.
__init__
(
name
,
options
,
data_type
=
'datetimepicker'
)
def
validate
(
self
,
value
):
# TODO: Validation
return
True
def
convert
(
*
args
):
def
_inner
(
func
):
print
args
func
.
_converter_for
=
args
return
func
return
_inner
class
BaseFilterConverter
(
object
):
def
__init__
(
self
):
self
.
converters
=
dict
()
for
p
in
dir
(
self
):
attr
=
getattr
(
self
,
p
)
if
hasattr
(
attr
,
'_converter_for'
):
for
p
in
attr
.
_converter_for
:
self
.
converters
[
p
]
=
attr
flask_adminex/static/css/admin.css
View file @
73ed6524
/*
Body
*/
/*
Global styles
*/
body
body
{
{
padding-top
:
50px
;
padding-top
:
50px
;
}
}
/* Form customizations */
form
.icon
{
form
.icon
{
display
:
inline
;
display
:
inline
;
}
}
...
@@ -19,3 +20,18 @@ form.icon button {
...
@@ -19,3 +20,18 @@ form.icon button {
a
.icon
{
a
.icon
{
text-decoration
:
none
;
text-decoration
:
none
;
}
}
/* Filters */
.filter-row
{
margin
:
4px
;
}
.filter-row
a
,
.filter-row
select
{
margin-right
:
4px
;
}
.filter-row
input
{
margin-bottom
:
0px
;
width
:
208px
;
}
\ No newline at end of file
flask_adminex/static/js/filters.js
0 → 100644
View file @
73ed6524
var
Filters
=
function
(
element
,
operations
,
options
,
types
)
{
var
$root
=
$
(
element
)
var
$container
=
$
(
'#filters'
);
var
count
=
$
(
'#filters>div'
,
$root
).
length
;
function
appendValueControl
(
element
,
id
,
optionId
)
{
var
field
;
// Conditionally generate select or textbox
if
(
optionId
in
options
)
{
field
=
$
(
'<select class="filter-val" />'
).
attr
(
'name'
,
'flt'
+
id
+
'v'
);
$
(
options
[
optionId
]).
each
(
function
()
{
field
.
append
(
$
(
'<option/>'
).
val
(
this
[
0
]).
text
(
this
[
1
]));
});
}
else
{
field
=
$
(
'<input type="text" class="filter-val" />'
).
attr
(
'name'
,
'flt'
+
id
+
'v'
);
}
$
(
element
).
append
(
field
);
if
(
optionId
in
options
)
field
.
chosen
();
if
(
optionId
in
types
)
{
field
.
attr
(
'data-role'
,
types
[
optionId
]);
adminForm
.
applyStyle
(
field
,
types
[
optionId
]);
}
}
function
addFilter
()
{
var
node
=
$
(
'<div class="filter-row" />'
).
attr
(
'id'
,
'fltdiv'
+
count
).
appendTo
(
$container
);
$
(
'<a href="#" class="remove-filter" />'
)
.
append
(
'<i class="icon-remove"/>'
)
.
click
(
removeFilter
)
.
appendTo
(
node
);
var
operation
=
$
(
'<select class="filter-op" />'
)
.
attr
(
'name'
,
'flt'
+
count
)
.
change
(
changeOperation
)
.
appendTo
(
node
);
var
index
=
0
;
$
(
operations
).
each
(
function
()
{
operation
.
append
(
$
(
'<option/>'
).
val
(
index
).
text
(
this
.
toString
()));
index
++
;
});
operation
.
chosen
();
appendValueControl
(
node
,
count
,
0
);
count
+=
1
;
$
(
'button'
,
$root
).
show
();
return
false
;
}
function
removeFilter
()
{
var
row
=
$
(
this
).
parent
();
var
idx
=
parseInt
(
row
.
attr
(
'id'
).
substr
(
6
));
// Remove row
row
.
remove
();
// Renumber any rows that are after
for
(
var
i
=
idx
+
1
;
i
<
count
;
++
i
)
{
row
=
$
(
'#fltdiv'
+
i
);
row
.
attr
(
'id'
,
'fltdiv'
+
(
i
-
1
));
$
(
'.filter-op'
,
row
).
attr
(
'name'
,
'flt'
+
(
i
-
1
));
$
(
'.filter-val'
,
row
).
attr
(
'name'
,
'flt'
+
(
i
-
1
)
+
'v'
);
}
count
-=
1
;
$
(
'button'
,
$root
).
show
();
return
false
;
}
function
changeOperation
()
{
var
row
=
$
(
this
).
parent
();
var
rowIdx
=
parseInt
(
row
.
attr
(
'id'
).
substr
(
6
));
// Get old value field
var
oldValue
=
$
(
'.filter-val'
,
row
);
var
oldValueId
=
oldValue
.
attr
(
'id'
);
// Delete old value
oldValue
.
remove
();
if
(
oldValueId
!=
null
)
$
(
'div#'
+
oldValueId
+
'_chzn'
,
row
).
remove
();
var
optId
=
$
(
this
).
val
();
appendValueControl
(
row
,
rowIdx
,
optId
);
$
(
'button'
,
$root
).
show
();
};
$
(
'#add_filter'
,
$root
).
click
(
addFilter
);
$
(
'.remove-filter'
,
$root
).
click
(
removeFilter
);
$
(
'.filter-op'
).
change
(
changeOperation
);
$
(
'.filter-val'
).
change
(
function
()
{
$
(
'button'
,
$root
).
show
();
});
};
flask_adminex/static/js/form.js
View file @
73ed6524
$
(
function
()
{
var
adminForm
=
new
function
()
{
this
.
applyStyle
=
function
(
el
,
name
)
{
switch
(
name
)
{
case
'chosen'
:
$
(
el
).
chosen
();
break
;
case
'chosenblank'
:
$
(
el
).
chosen
({
allow_single_deselect
:
true
});
break
;
case
'datepicker'
:
$
(
el
).
datepicker
();
break
;
case
'datetimepicker'
:
$
(
el
).
datepicker
({
displayTime
:
true
});
break
;
};
}
// Apply automatic styles
$
(
'[data-role=chosen]'
).
chosen
();
$
(
'[data-role=chosen]'
).
chosen
();
$
(
'[data-role=chosenblank]'
).
chosen
({
allow_single_deselect
:
true
});
$
(
'[data-role=chosenblank]'
).
chosen
({
allow_single_deselect
:
true
});
$
(
'[data-role=datepicker]'
).
datepicker
();
$
(
'[data-role=datepicker]'
).
datepicker
();
$
(
'[data-role=datetimepicker]'
).
datepicker
({
displayTime
:
true
});
$
(
'[data-role=datetimepicker]'
).
datepicker
({
displayTime
:
true
});
}
);
}
flask_adminex/templates/admin/model/list.html
View file @
73ed6524
{% extends 'admin/master.html' %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib %}
{% import 'admin/lib.html' as lib %}
{% block head %}
<link
href=
"{{ url_for('admin.static', filename='chosen/chosen.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/datepicker.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% block body %}
{% block body %}
{% if search_supported %}
{% if search_supported %}
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"well form-search"
>
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"well form-search"
>
...
@@ -20,6 +25,38 @@
...
@@ -20,6 +25,38 @@
</form>
</form>
{% endif %}
{% endif %}
{% if filter_names %}
<form
id=
"filter_form"
method=
"GET"
action=
"{{ return_url }}"
class=
"well"
>
<div
id=
"filters"
>
{%- for idx, flt in enumerate(active_filters) -%}
<div
id=
"fltdiv{{ idx }}"
class=
"filter-row"
>
<a
href=
"#"
class=
"remove-filter"
><i
class=
"icon-remove"
></i></a><select
name=
"flt{{ idx }}"
class=
"filter-op"
data-role=
"chosen"
>
{% for optidx, opt in enumerate(filter_names) -%}
<option
value=
"{{ optidx }}"
{%
if
flt
[
0
]
==
optidx
%}
selected=
"selected"
{%
endif
%}
>
{{ opt }}
</option>
{%- endfor %}
</select>
{%- set data = filter_data.get(flt[0]) -%}
{%- if data -%}
<select
name=
"flt{{ idx }}v"
class=
"filter-val"
data-role=
"chosen"
>
{%- for opt in data %}
<option
value=
"{{ opt[0] }}"
{%
if
flt
[
1
]
==
opt
[
0
]
%}
selected
{%
endif
%}
>
{{ opt[1] }}
</option>
{%- endfor %}
</select>
{%- else -%}
<input
name=
"flt{{ idx }}v"
type=
"text"
value=
"{{ flt[1] or '' }}"
class=
"filter-val"
{%
if
flt
[
0
]
in
filter_types
%}
data-role=
"{{ filter_types[flt[0]] }}"
{%
endif
%}
></input>
{%- endif -%}
</div>
{%- endfor %}
</div>
{% if active_filters %}
<a
href=
"{{ clear_search_url }}"
class=
"btn"
>
Reset Filters
</a>
{% endif %}
<a
id=
"add_filter"
href=
"#"
class=
"btn"
>
Add Filter
</a>
<button
type=
"submit"
class=
"btn"
style=
"display: none"
>
Apply
</button>
</form>
{% endif %}
<table
class=
"table table-striped table-bordered model-list"
>
<table
class=
"table table-striped table-bordered model-list"
>
<thead>
<thead>
<tr>
<tr>
...
@@ -75,3 +112,17 @@
...
@@ -75,3 +112,17 @@
<a
class=
"btn btn-primary btn-large"
href=
"{{ url_for('.create_view', url=return_url) }}"
>
Create New
</a>
<a
class=
"btn btn-primary btn-large"
href=
"{{ url_for('.create_view', url=return_url) }}"
>
Create New
</a>
{% endif %}
{% endif %}
{% endblock %}
{% endblock %}
{% block tail %}
<script
src=
"{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/form.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/filters.js') }}"
></script>
{% if filter_names is not none and filter_data is not none %}
<script
language=
"javascript"
>
var
filter
=
new
Filters
(
'#filter_form'
,
{{
filter_names
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_types
|
tojson
|
safe
}});
</script>
{% endif %}
{% endblock %}
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