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
1db59b00
Commit
1db59b00
authored
Apr 04, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reworked filter UI (got idea from flask-peewee)
parent
eb50adc8
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
257 additions
and
188 deletions
+257
-188
TODO.txt
TODO.txt
+7
-1
filters.py
flask_adminex/ext/sqlamodel/filters.py
+12
-12
base.py
flask_adminex/model/base.py
+49
-31
filters.py
flask_adminex/model/filters.py
+8
-0
chosen.css
flask_adminex/static/chosen/chosen.css
+1
-0
admin.css
flask_adminex/static/css/admin.css
+9
-0
filters.js
flask_adminex/static/js/filters.js
+68
-82
form.js
flask_adminex/static/js/form.js
+2
-2
create.html
flask_adminex/templates/admin/model/create.html
+8
-0
edit.html
flask_adminex/templates/admin/model/edit.html
+1
-1
list.html
flask_adminex/templates/admin/model/list.html
+77
-49
test_model.py
flask_adminex/tests/test_model.py
+5
-1
test_sqlamodel.py
flask_adminex/tests/test_sqlamodel.py
+10
-9
No files found.
TODO.txt
View file @
1db59b00
- Core
- Core
- Pregenerate URLs for menu
- Pregenerate URLs for menu
- Calendar - add validation for time without seconds (automatically add seconds)
- Calendar - add validation for time without seconds (automatically add seconds)
- View Site button?
- Model Admin
- Model Admin
- Rework filter UI
- Rework model UI
- Number of records
- Tabs
- Filters in drop down instead of add button
- Ability to sort by fields that are not visible?
- Ability to sort by fields that are not visible?
- List display callables
- List display callables
- Search
- Search
- Rename init_search
- Rename init_search
- Filters
- FK filters support
- Paginator class
- Paginator class
- Custom CSS/JS in admin interface
- Custom CSS/JS in admin interface
- Checkboxes and mass operations
- Checkboxes and mass operations
...
...
flask_adminex/ext/sqlamodel/filters.py
View file @
1db59b00
...
@@ -29,16 +29,16 @@ class FilterEqual(BaseSQLAFilter):
...
@@ -29,16 +29,16 @@ class FilterEqual(BaseSQLAFilter):
def
apply
(
self
,
query
,
value
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
==
value
)
return
query
.
filter
(
self
.
column
==
value
)
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s equals'
%
self
.
name
return
'
equals'
class
FilterNotEqual
(
BaseSQLAFilter
):
class
FilterNotEqual
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
!=
value
)
return
query
.
filter
(
self
.
column
!=
value
)
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s not equal'
%
self
.
name
return
'
not equal'
class
FilterLike
(
BaseSQLAFilter
):
class
FilterLike
(
BaseSQLAFilter
):
...
@@ -46,8 +46,8 @@ class FilterLike(BaseSQLAFilter):
...
@@ -46,8 +46,8 @@ class FilterLike(BaseSQLAFilter):
stmt
=
tools
.
parse_like_term
(
value
)
stmt
=
tools
.
parse_like_term
(
value
)
return
query
.
filter
(
self
.
column
.
ilike
(
stmt
))
return
query
.
filter
(
self
.
column
.
ilike
(
stmt
))
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s like'
%
self
.
name
return
'
like'
class
FilterNotLike
(
BaseSQLAFilter
):
class
FilterNotLike
(
BaseSQLAFilter
):
...
@@ -55,24 +55,24 @@ class FilterNotLike(BaseSQLAFilter):
...
@@ -55,24 +55,24 @@ class FilterNotLike(BaseSQLAFilter):
stmt
=
tools
.
parse_like_term
(
value
)
stmt
=
tools
.
parse_like_term
(
value
)
return
query
.
filter
(
~
self
.
column
.
ilike
(
stmt
))
return
query
.
filter
(
~
self
.
column
.
ilike
(
stmt
))
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s not like'
%
self
.
name
return
'
not like'
class
FilterGreater
(
BaseSQLAFilter
):
class
FilterGreater
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
>
value
)
return
query
.
filter
(
self
.
column
>
value
)
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s greater than'
%
self
.
name
return
'
greater than'
class
FilterSmaller
(
BaseSQLAFilter
):
class
FilterSmaller
(
BaseSQLAFilter
):
def
apply
(
self
,
query
,
value
):
def
apply
(
self
,
query
,
value
):
return
query
.
filter
(
self
.
column
<
value
)
return
query
.
filter
(
self
.
column
<
value
)
def
__unicode__
(
self
):
def
operation
(
self
):
return
'
%
s smaller than'
%
self
.
name
return
'
smaller than'
# Customized type filters
# Customized type filters
...
...
flask_adminex/model/base.py
View file @
1db59b00
from
itertools
import
count
from
flask
import
request
,
url_for
,
redirect
,
flash
from
flask
import
request
,
url_for
,
redirect
,
flash
from
flask.ext.adminex.base
import
BaseView
,
expose
from
flask.ext.adminex.base
import
BaseView
,
expose
...
@@ -222,12 +220,24 @@ class BaseModelView(BaseView):
...
@@ -222,12 +220,24 @@ class BaseModelView(BaseView):
self
.
_filters
=
self
.
get_filters
()
self
.
_filters
=
self
.
get_filters
()
if
self
.
_filters
:
if
self
.
_filters
:
self
.
_filter_names
=
[
unicode
(
n
)
for
n
in
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
))
else
:
group
=
self
.
_filter_dict
[
n
.
name
]
group
.
append
((
i
,
n
.
operation
()))
self
.
_filter_types
=
dict
((
i
,
f
.
data_type
)
self
.
_filter_types
=
dict
((
i
,
f
.
data_type
)
for
i
,
f
in
enumerate
(
self
.
_filters
)
for
i
,
f
in
enumerate
(
self
.
_filters
)
if
f
.
data_type
)
if
f
.
data_type
)
else
:
else
:
self
.
_filter_
name
s
=
None
self
.
_filter_
group
s
=
None
self
.
_filter_types
=
None
self
.
_filter_types
=
None
# Primary key
# Primary key
...
@@ -518,21 +528,29 @@ class BaseModelView(BaseView):
...
@@ -518,21 +528,29 @@ class BaseModelView(BaseView):
# Gather filters
# Gather filters
if
self
.
_filters
:
if
self
.
_filters
:
filters
=
[]
s
filters
=
[]
for
n
in
count
():
for
n
in
request
.
args
:
param
=
'flt
%
d'
%
n
if
n
.
startswith
(
'flt'
):
if
param
not
in
request
.
args
:
ofs
=
n
.
find
(
'_'
)
break
if
ofs
==
-
1
:
continue
idx
=
request
.
args
.
get
(
param
,
None
,
type
=
int
)
try
:
value
=
request
.
args
.
get
(
param
+
'v'
,
None
)
pos
=
int
(
n
[
3
:
ofs
])
idx
=
int
(
n
[
ofs
+
1
:])
except
ValueError
:
continue
if
idx
>=
0
and
idx
<
len
(
self
.
_filters
):
if
idx
>=
0
and
idx
<
len
(
self
.
_filters
):
flt
=
self
.
_filters
[
idx
]
flt
=
self
.
_filters
[
idx
]
value
=
request
.
args
[
n
]
if
flt
.
validate
(
value
):
if
flt
.
validate
(
value
):
filters
.
append
((
idx
,
flt
.
clean
(
value
)))
sfilters
.
append
((
pos
,
(
idx
,
flt
.
clean
(
value
))))
filters
=
[
v
[
1
]
for
v
in
sorted
(
sfilters
,
key
=
lambda
n
:
n
[
0
])]
else
:
else
:
filters
=
None
filters
=
None
...
@@ -567,10 +585,8 @@ class BaseModelView(BaseView):
...
@@ -567,10 +585,8 @@ class BaseModelView(BaseView):
if
filters
:
if
filters
:
for
i
,
flt
in
enumerate
(
filters
):
for
i
,
flt
in
enumerate
(
filters
):
base
=
'flt
%
d'
%
i
key
=
'flt
%
d_
%
d'
%
(
i
,
flt
[
0
])
kwargs
[
key
]
=
flt
[
1
]
kwargs
[
base
]
=
flt
[
0
]
kwargs
[
base
+
'v'
]
=
flt
[
1
]
return
url_for
(
view
,
**
kwargs
)
return
url_for
(
view
,
**
kwargs
)
...
@@ -646,6 +662,7 @@ class BaseModelView(BaseView):
...
@@ -646,6 +662,7 @@ class BaseModelView(BaseView):
search
,
search
,
filters
),
filters
),
# Pagination
# Pagination
count
=
count
,
pager_url
=
pager_url
,
pager_url
=
pager_url
,
num_pages
=
num_pages
,
num_pages
=
num_pages
,
page
=
page
,
page
=
page
,
...
@@ -661,7 +678,8 @@ class BaseModelView(BaseView):
...
@@ -661,7 +678,8 @@ class BaseModelView(BaseView):
sort_desc
),
sort_desc
),
search
=
search
,
search
=
search
,
# Filters
# Filters
filter_names
=
self
.
_filter_names
,
filters
=
self
.
_filters
,
filter_groups
=
self
.
_filter_groups
,
filter_types
=
self
.
_filter_types
,
filter_types
=
self
.
_filter_types
,
filter_data
=
filters_data
,
filter_data
=
filters_data
,
active_filters
=
filters
active_filters
=
filters
...
@@ -672,10 +690,10 @@ class BaseModelView(BaseView):
...
@@ -672,10 +690,10 @@ class BaseModelView(BaseView):
"""
"""
Create model view
Create model view
"""
"""
return_url
=
request
.
args
.
get
(
'url'
)
return_url
=
request
.
args
.
get
(
'url'
)
or
url_for
(
'.index_view'
)
if
not
self
.
can_create
:
if
not
self
.
can_create
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
form
=
self
.
create_form
(
request
.
form
)
form
=
self
.
create_form
(
request
.
form
)
...
@@ -685,7 +703,7 @@ class BaseModelView(BaseView):
...
@@ -685,7 +703,7 @@ class BaseModelView(BaseView):
flash
(
'Model was successfully created.'
)
flash
(
'Model was successfully created.'
)
return
redirect
(
url_for
(
'.create_view'
,
url
=
return_url
))
return
redirect
(
url_for
(
'.create_view'
,
url
=
return_url
))
else
:
else
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
return
self
.
render
(
self
.
create_template
,
return
self
.
render
(
self
.
create_template
,
form
=
form
,
form
=
form
,
...
@@ -696,40 +714,40 @@ class BaseModelView(BaseView):
...
@@ -696,40 +714,40 @@ class BaseModelView(BaseView):
"""
"""
Edit model view
Edit model view
"""
"""
return_url
=
request
.
args
.
get
(
'url'
)
return_url
=
request
.
args
.
get
(
'url'
)
or
url_for
(
'.index_view'
)
if
not
self
.
can_edit
:
if
not
self
.
can_edit
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
model
=
self
.
get_one
(
id
)
model
=
self
.
get_one
(
id
)
if
model
is
None
:
if
model
is
None
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
form
=
self
.
edit_form
(
request
.
form
,
model
)
form
=
self
.
edit_form
(
request
.
form
,
model
)
if
form
.
validate_on_submit
():
if
form
.
validate_on_submit
():
if
self
.
update_model
(
form
,
model
):
if
self
.
update_model
(
form
,
model
):
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
return
self
.
render
(
self
.
edit_template
,
return
self
.
render
(
self
.
edit_template
,
form
=
form
,
form
=
form
,
return_url
=
return_url
or
url_for
(
'.index_view'
)
)
return_url
=
return_url
)
@
expose
(
'/delete/<int:id>/'
,
methods
=
(
'POST'
,))
@
expose
(
'/delete/<int:id>/'
,
methods
=
(
'POST'
,))
def
delete_view
(
self
,
id
):
def
delete_view
(
self
,
id
):
"""
"""
Delete model view. Only POST method is allowed.
Delete model view. Only POST method is allowed.
"""
"""
return_url
=
request
.
args
.
get
(
'url'
)
return_url
=
request
.
args
.
get
(
'url'
)
or
url_for
(
'.index_view'
)
# TODO: Use post
# TODO: Use post
if
not
self
.
can_delete
:
if
not
self
.
can_delete
:
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
model
=
self
.
get_one
(
id
)
model
=
self
.
get_one
(
id
)
if
model
:
if
model
:
self
.
delete_model
(
model
)
self
.
delete_model
(
model
)
return
redirect
(
return_url
or
url_for
(
'.index_view'
)
)
return
redirect
(
return_url
)
flask_adminex/model/filters.py
View file @
1db59b00
...
@@ -57,6 +57,14 @@ class BaseFilter(object):
...
@@ -57,6 +57,14 @@ class BaseFilter(object):
"""
"""
raise
NotImplemented
()
raise
NotImplemented
()
def
operation
(
self
):
"""
Return readable operation name.
For example: u'equals'
"""
raise
NotImplemented
()
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
self
.
name
return
self
.
name
...
...
flask_adminex/static/chosen/chosen.css
View file @
1db59b00
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
display
:
inline-block
;
display
:
inline-block
;
zoom
:
1
;
zoom
:
1
;
*
display
:
inline
;
*
display
:
inline
;
vertical-align
:
middle
;
}
}
.chzn-container
.chzn-drop
{
.chzn-container
.chzn-drop
{
background
:
#fff
;
background
:
#fff
;
...
...
flask_adminex/static/css/admin.css
View file @
1db59b00
...
@@ -21,6 +21,15 @@ a.icon {
...
@@ -21,6 +21,15 @@ a.icon {
text-decoration
:
none
;
text-decoration
:
none
;
}
}
/* Model search form */
form
.search-form
{
margin
:
4px
0
0
0
;
}
form
.search-form
a
.clear
i
{
margin
:
2px
0
0
0
;
}
/* Filters */
/* Filters */
.filter-row
{
.filter-row
{
margin
:
4px
;
margin
:
4px
;
...
...
flask_adminex/static/js/filters.js
View file @
1db59b00
var
Filters
=
function
(
element
,
operations
,
options
,
types
)
{
var
AdminFilters
=
function
(
element
,
filters_element
,
adminForm
,
operations
,
options
,
types
)
{
var
$root
=
$
(
element
)
var
$root
=
$
(
element
)
var
$container
=
$
(
'
#filters'
);
var
$container
=
$
(
'
.filters'
,
$root
);
var
count
=
$
(
'#filters>div'
,
$root
).
length
;
var
lastCount
=
0
;
function
appendValueControl
(
element
,
id
,
optionId
)
{
function
getCount
(
name
)
{
var
field
;
var
idx
=
name
.
indexOf
(
'_'
);
return
parseInt
(
name
.
substr
(
3
,
idx
-
3
));
// 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
);
function
changeOperation
()
{
var
$parent
=
$
(
this
).
parent
();
if
(
optionId
in
options
)
var
$el
=
$
(
'.filter-val'
,
$parent
);
field
.
chosen
();
var
count
=
getCount
(
$el
.
attr
(
'name'
));
$el
.
attr
(
'name'
,
'flt'
+
count
+
'_'
+
$
(
this
).
val
());
if
(
optionId
in
types
)
{
$
(
'button'
,
$root
).
show
();
field
.
attr
(
'data-role'
,
types
[
optionId
]);
adminForm
.
applyStyle
(
field
,
types
[
optionId
]);
}
}
function
removeFilter
()
{
$
(
this
).
parent
().
remove
();
$
(
'button'
,
$root
).
show
();
}
}
function
addFilter
()
{
function
addFilter
(
name
,
op
)
{
var
node
=
$
(
'<div class="filter-row" />'
).
attr
(
'id'
,
'fltdiv'
+
count
).
appendTo
(
$container
);
var
$el
=
$
(
'<div class="filter-row" />'
).
appendTo
(
$container
);
$
(
'<a href="#" class="
remove-f
ilter" />'
)
$
(
'<a href="#" class="
btn remove-filter" title="Remove F
ilter" />'
)
.
append
(
'<i class="icon-remove"/>'
)
.
text
(
name
)
.
click
(
removeFilter
)
.
appendTo
(
$el
)
.
appendTo
(
node
);
.
click
(
removeFilter
);
var
operation
=
$
(
'<select class="filter-op" />'
)
var
$select
=
$
(
'<select class="filter-op" />'
)
.
attr
(
'name'
,
'flt'
+
count
)
.
appendTo
(
$el
)
.
change
(
changeOperation
)
.
change
(
changeOperation
);
.
appendTo
(
node
);
var
index
=
0
;
$
(
op
).
each
(
function
()
{
$
(
operations
).
each
(
function
()
{
$select
.
append
(
$
(
'<option/>'
).
attr
(
'value'
,
this
[
0
]).
text
(
this
[
1
]));
operation
.
append
(
$
(
'<option/>'
).
val
(
index
).
text
(
this
.
toString
()));
index
++
;
});
});
operation
.
chosen
();
appendValueControl
(
node
,
count
,
0
);
count
+=
1
;
$select
.
chosen
()
;
$
(
'button'
,
$root
).
show
();
var
optId
=
op
[
0
][
0
];
return
false
;
}
function
removeFilter
()
{
var
$field
;
var
row
=
$
(
this
).
parent
();
var
idx
=
parseInt
(
row
.
attr
(
'id'
).
substr
(
6
));
// Remove row
if
(
optId
in
options
)
{
row
.
remove
();
$field
=
$
(
'<select class="filter-val" />'
)
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
optId
)
.
appendTo
(
$el
);
// Renumber any rows that are after
$
(
options
[
optId
]).
each
(
function
()
{
for
(
var
i
=
idx
+
1
;
i
<
count
;
++
i
)
{
$field
.
append
(
$
(
'<option/>'
)
row
=
$
(
'#fltdiv'
+
i
);
.
val
(
this
[
0
]).
text
(
this
[
1
]))
row
.
attr
(
'id'
,
'fltdiv'
+
(
i
-
1
));
.
appendTo
(
$el
);
});
$
(
'.filter-op'
,
row
).
attr
(
'name'
,
'flt'
+
(
i
-
1
));
$field
.
chosen
();
$
(
'.filter-val'
,
row
).
attr
(
'name'
,
'flt'
+
(
i
-
1
)
+
'v'
);
}
else
{
$field
=
$
(
'<input type="text" class="filter-val" />'
)
.
attr
(
'name'
,
'flt'
+
lastCount
+
'_'
+
optId
)
.
appendTo
(
$el
);
}
}
count
-=
1
;
if
(
optId
in
types
)
{
$field
.
attr
(
'data-role'
,
types
[
optId
]);
$
(
'button'
,
$root
).
show
();
adminForm
.
applyStyle
(
$field
,
types
[
optId
]);
return
false
;
}
}
function
changeOperation
()
{
lastCount
+=
1
;
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
$
(
'a.filter'
,
filters_element
).
click
(
function
()
{
oldValue
.
remove
();
var
name
=
$
(
this
).
text
().
trim
();
if
(
oldValueId
!=
null
)
$
(
'div#'
+
oldValueId
+
'_chzn'
,
row
).
remove
();
var
optId
=
$
(
this
).
val
();
addFilter
(
name
,
operations
[
name
]);
appendValueControl
(
row
,
rowIdx
,
optId
);
$
(
'button'
,
$root
).
show
();
$
(
'button'
,
$root
).
show
();
};
}
)
;
$
(
'#add_filter'
,
$root
).
click
(
addFilter
);
$
(
'.filter-op'
,
$root
).
change
(
changeOperation
);
$
(
'.remove-filter'
,
$root
).
click
(
removeFilter
);
$
(
'.filter-val'
,
$root
).
change
(
function
()
{
$
(
'.filter-op'
).
change
(
changeOperation
);
$
(
'.filter-val'
).
change
(
function
()
{
$
(
'button'
,
$root
).
show
();
$
(
'button'
,
$root
).
show
();
});
});
$
(
'.remove-filter'
,
$root
).
click
(
removeFilter
);
$
(
'.filter-val'
,
$root
).
each
(
function
()
{
var
count
=
getCount
(
$
(
this
).
attr
(
'name'
));
if
(
count
>
lastCount
)
lastCount
=
count
;
});
lastCount
+=
1
;
};
};
flask_adminex/static/js/form.js
View file @
1db59b00
var
adminForm
=
new
function
()
{
var
AdminForm
=
function
()
{
this
.
applyStyle
=
function
(
el
,
name
)
{
this
.
applyStyle
=
function
(
el
,
name
)
{
switch
(
name
)
{
switch
(
name
)
{
case
'chosen'
:
case
'chosen'
:
...
@@ -21,4 +21,4 @@ var adminForm = new function() {
...
@@ -21,4 +21,4 @@ var adminForm = new function() {
$
(
'[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/create.html
View file @
1db59b00
...
@@ -11,6 +11,14 @@
...
@@ -11,6 +11,14 @@
<input
name=
"_add_another"
type=
"submit"
class=
"btn btn-primary btn-large"
value=
"Save and Add"
/>
<input
name=
"_add_another"
type=
"submit"
class=
"btn btn-primary btn-large"
value=
"Save and Add"
/>
{% endmacro %}
{% endmacro %}
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
"{{ return_url }}"
>
List
</a>
</li>
<li
class=
"active"
>
<a
href=
"#"
>
Create
</a>
</li>
</ul>
{{ lib.render_form(form, return_url, extra()) }}
{{ lib.render_form(form, return_url, extra()) }}
{% endblock %}
{% endblock %}
...
...
flask_adminex/templates/admin/model/edit.html
View file @
1db59b00
flask_adminex/templates/admin/model/list.html
View file @
1db59b00
...
@@ -7,53 +7,83 @@
...
@@ -7,53 +7,83 @@
{% endblock %}
{% endblock %}
{% block body %}
{% block body %}
{% if search_supported %}
<ul
class=
"nav nav-tabs"
>
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"well form-search"
>
<li
class=
"active"
>
{% if search %}
<a
href=
"#"
>
List ({{ count }})
</a>
<a
href=
"{{ clear_search_url }}"
>
</li>
<i
class=
"icon-remove"
></i>
{% if admin_view.can_create %}
<li>
<a
href=
"{{ url_for('.create_view', url=return_url) }}"
>
Create
</a>
</li>
{% endif %}
{% if filter_groups %}
<li
class=
"dropdown"
>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
Add Filter
<b
class=
"caret"
></b>
</a>
</a>
<ul
class=
"dropdown-menu field-filters"
>
{% for k in filter_groups %}
<li>
<a
href=
"#"
class=
"filter"
>
{{ k[0] }}
</a>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endif %}
{% if search_supported %}
<li>
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"search-form"
>
{% if sort_column is not none %}
{% if sort_column is not none %}
<input
type=
"hidden"
name=
"sort"
value=
"{{ sort_column }}"
></input>
<input
type=
"hidden"
name=
"sort"
value=
"{{ sort_column }}"
></input>
{% endif %}
{% endif %}
{% if sort_desc %}
{% if sort_desc %}
<input
type=
"hidden"
name=
"desc"
value=
"{{ sort_desc }}"
></input>
<input
type=
"hidden"
name=
"desc"
value=
"{{ sort_desc }}"
></input>
{% endif %}
{% endif %}
<input
type=
"text"
name=
"search"
value=
"{{ search or '' }}"
class=
"span10 search-query"
></input>
<input
type=
"text"
name=
"search"
value=
"{{ search or '' }}"
class=
"search-query span2"
placeholder=
"Search"
></input>
<button
type=
"submit"
class=
"btn"
>
Search
</button>
{% if search %}
<a
href=
"{{ clear_search_url }}"
class=
"clear"
>
<i
class=
"icon-remove"
></i>
</a>
{% endif %}
</form>
</form>
</li>
{% endif %}
{% endif %}
</ul>
{% if filter_groups %}
<form
id=
"filter_form"
method=
"GET"
action=
"{{ return_url }}"
>
<div
class=
"pull-right"
>
<button
type=
"submit"
class=
"btn btn-primary"
style=
"display: none"
>
Apply
</button>
{% if active_filters %}
<a
href=
"{{ clear_search_url }}"
class=
"btn"
>
Reset Filters
</a>
{% endif %}
</div>
{% if filter_names %}
<div
class=
"filters"
>
<form
id=
"filter_form"
method=
"GET"
action=
"{{ return_url }}"
class=
"well"
>
{%- for i, flt in enumerate(active_filters) -%}
<div
id=
"filters"
>
<div
class=
"filter-row"
>
{%- for idx, flt in enumerate(active_filters) -%}
{% set filter = admin_view._filters[flt[0]] %}
<div
id=
"fltdiv{{ idx }}"
class=
"filter-row"
>
<a
href=
"#"
class=
"btn remove-filter"
title=
"Remove Filter"
>
<a
href=
"#"
class=
"remove-filter"
><i
class=
"icon-remove"
></i></a><select
name=
"flt{{ idx }}"
class=
"filter-op"
data-role=
"chosen"
>
{{ filters[flt[0]] }}
{% for optidx, opt in enumerate(filter_names) -%}
</a><select
class=
"filter-op"
data-role=
"chosen"
>
<option
value=
"{{ optidx }}"
{%
if
flt
[
0
]
==
optidx
%}
selected=
"selected"
{%
endif
%}
>
{{ opt }}
</option>
{% for op in admin_view._filter_dict[filter.name] %}
{%- endfor %}
<option
value=
"{{ op[0] }}"
{%
if
flt
[
0
]
==
op
[
0
]
%}
selected=
"selected"
{%
endif
%}
>
{{ op[1] }}
</option>
{% endfor %}
</select>
</select>
{%- set data = filter_data.get(flt[0]) -%}
{%- set data = filter_data.get(flt[0]) -%}
{%- if data -%}
{%- if data -%}
<select
name=
"flt{{ idx }}v
"
class=
"filter-val"
data-role=
"chosen"
>
<select
name=
"flt{{ i }}_{{ flt[0] }}
"
class=
"filter-val"
data-role=
"chosen"
>
{%- for opt
in data %}
{%- for d
in data %}
<option
value=
"{{ opt[0] }}"
{%
if
flt
[
1
]
==
opt
[
0
]
%}
selected
{%
endif
%}
>
{{ opt
[1] }}
</option>
<option
value=
"{{ d[0] }}"
{%
if
flt
[
1
]
==
d
[
0
]
%}
selected
{%
endif
%}
>
{{ d
[1] }}
</option>
{%- endfor %}
{%- endfor %}
</select>
</select>
{%- else -%}
{%- 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>
<input
name=
"flt{{ i }}_{{ flt[0] }}
"
type=
"text"
value=
"{{ flt[1] or '' }}"
class=
"filter-val"
{%
if
flt
[
0
]
in
filter_types
%}
data-role=
"{{ filter_types[flt[0]] }}"
{%
endif
%}
></input>
{%- endif -%}
{%- endif -%}
</div>
</div>
{%-
endfor %}
{%
endfor %}
</div>
</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>
</form>
{% endif %}
{% endif %}
...
@@ -108,19 +138,17 @@
...
@@ -108,19 +138,17 @@
{% endfor %}
{% endfor %}
</table>
</table>
{{ lib.pager(page, num_pages, pager_url) }}
{{ lib.pager(page, num_pages, pager_url) }}
{% if admin_view.can_create %}
<a
class=
"btn btn-primary btn-large"
href=
"{{ url_for('.create_view', url=return_url) }}"
>
Create New
</a>
{% endif %}
{% endblock %}
{% endblock %}
{% block tail %}
{% block tail %}
<script
src=
"{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"
></script>
<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/form.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/filters.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/filters.js') }}"
></script>
{% if filter_
name
s is not none and filter_data is not none %}
{% if filter_
group
s is not none and filter_data is not none %}
<script
language=
"javascript"
>
<script
language=
"javascript"
>
var
filter
=
new
Filters
(
'#filter_form'
,
var
form
=
new
AdminForm
();
{{
filter_names
|
tojson
|
safe
}},
var
filter
=
new
AdminFilters
(
'#filter_form'
,
'.field-filters'
,
form
,
{{
admin_view
.
_filter_dict
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_types
|
tojson
|
safe
}});
{{
filter_types
|
tojson
|
safe
}});
</script>
</script>
...
...
flask_adminex/tests/test_model.py
View file @
1db59b00
...
@@ -28,6 +28,9 @@ class SimpleFilter(filters.BaseFilter):
...
@@ -28,6 +28,9 @@ class SimpleFilter(filters.BaseFilter):
query
.
_applied
=
True
query
.
_applied
=
True
return
query
return
query
def
operation
(
self
):
return
'test'
class
MockModelView
(
base
.
BaseModelView
):
class
MockModelView
(
base
.
BaseModelView
):
def
__init__
(
self
,
model
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
,
def
__init__
(
self
,
model
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
,
...
@@ -288,7 +291,8 @@ def test_column_filters():
...
@@ -288,7 +291,8 @@ def test_column_filters():
eq_
(
view
.
_filters
[
0
]
.
name
,
'col1'
)
eq_
(
view
.
_filters
[
0
]
.
name
,
'col1'
)
eq_
(
view
.
_filters
[
1
]
.
name
,
'col2'
)
eq_
(
view
.
_filters
[
1
]
.
name
,
'col2'
)
eq_
(
view
.
_filter_names
,
[
'col1'
,
'col2'
])
eq_
(
view
.
_filter_dict
,
{
'col1'
:
[(
0
,
'test'
)],
'col2'
:
[(
1
,
'test'
)]})
# TODO: Make calls with filters
# TODO: Make calls with filters
...
...
flask_adminex/tests/test_sqlamodel.py
View file @
1db59b00
...
@@ -215,8 +215,10 @@ def test_column_filters():
...
@@ -215,8 +215,10 @@ def test_column_filters():
eq_
(
len
(
view
.
_filters
),
4
)
eq_
(
len
(
view
.
_filters
),
4
)
eq_
(
view
.
_filter_names
,
[
'Test1 equals'
,
'Test1 not equal'
,
eq_
(
view
.
_filter_dict
,
{
'Test1'
:
[(
0
,
'equals'
),
'Test1 like'
,
'Test1 not like'
])
(
1
,
'not equal'
),
(
2
,
'like'
),
(
3
,
'not like'
)]})
db
.
session
.
add
(
Model1
(
'model1'
))
db
.
session
.
add
(
Model1
(
'model1'
))
db
.
session
.
add
(
Model1
(
'model2'
))
db
.
session
.
add
(
Model1
(
'model2'
))
...
@@ -226,12 +228,12 @@ def test_column_filters():
...
@@ -226,12 +228,12 @@ def test_column_filters():
client
=
app
.
test_client
()
client
=
app
.
test_client
()
rv
=
client
.
get
(
'/admin/model1view/?flt0
=0&flt0v
=model1'
)
rv
=
client
.
get
(
'/admin/model1view/?flt0
_0
=model1'
)
eq_
(
rv
.
status_code
,
200
)
eq_
(
rv
.
status_code
,
200
)
ok_
(
'model1'
in
rv
.
data
)
ok_
(
'model1'
in
rv
.
data
)
ok_
(
'model2'
not
in
rv
.
data
)
ok_
(
'model2'
not
in
rv
.
data
)
rv
=
client
.
get
(
'/admin/model1view/?flt0
=5
'
)
rv
=
client
.
get
(
'/admin/model1view/?flt0
_5=model1
'
)
eq_
(
rv
.
status_code
,
200
)
eq_
(
rv
.
status_code
,
200
)
ok_
(
'model1'
in
rv
.
data
)
ok_
(
'model1'
in
rv
.
data
)
ok_
(
'model2'
in
rv
.
data
)
ok_
(
'model2'
in
rv
.
data
)
...
@@ -241,10 +243,8 @@ def test_column_filters():
...
@@ -241,10 +243,8 @@ def test_column_filters():
column_filters
=
[
'int_field'
])
column_filters
=
[
'int_field'
])
admin
.
add_view
(
view
)
admin
.
add_view
(
view
)
eq_
(
view
.
_filter_names
,
[
'Int Field equals'
,
eq_
(
view
.
_filter_dict
,
{
'Int Field'
:
[(
0
,
'equals'
),
(
1
,
'not equal'
),
'Int Field not equal'
,
(
2
,
'greater than'
),
(
3
,
'smaller than'
)]})
'Int Field greater than'
,
'Int Field smaller than'
])
def
test_url_args
():
def
test_url_args
():
...
@@ -297,6 +297,7 @@ def test_url_args():
...
@@ -297,6 +297,7 @@ def test_url_args():
rv
=
client
.
get
(
'/admin/model1view/?flt0=1&flt0v=data1'
)
rv
=
client
.
get
(
'/admin/model1view/?flt0=1&flt0v=data1'
)
ok_
(
'data2'
in
rv
.
data
)
ok_
(
'data2'
in
rv
.
data
)
def
test_form
():
def
test_form
():
# TODO: form_columns
# TODO: form_columns
# TODO: excluded_form_columns
# TODO: excluded_form_columns
...
...
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