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
4049b93a
Commit
4049b93a
authored
Mar 19, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Sorting, paging and UI cleanup.
parent
e3aa36b8
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
225 additions
and
32 deletions
+225
-32
TODO.txt
TODO.txt
+0
-2
simple.py
examples/sqla/simple.py
+1
-1
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+111
-22
admin.css
flask_adminex/static/css/admin.css
+1
-0
lib.html
flask_adminex/templates/admin/lib.html
+62
-0
master.html
flask_adminex/templates/admin/master.html
+16
-0
edit.html
flask_adminex/templates/admin/model/edit.html
+1
-0
list.html
flask_adminex/templates/admin/model/list.html
+33
-7
No files found.
TODO.txt
View file @
4049b93a
...
@@ -5,8 +5,6 @@
...
@@ -5,8 +5,6 @@
- Ability to override displayed columns in a list
- Ability to override displayed columns in a list
- Ability to override displayed form fields
- Ability to override displayed form fields
- Ability to provide form validators without messing with form creation
- Ability to provide form validators without messing with form creation
- Paging
- Sorting
- Filtering
- Filtering
- Many2Many editing
- Many2Many editing
- Many2One editor
- Many2One editor
...
...
examples/sqla/simple.py
View file @
4049b93a
...
@@ -56,4 +56,4 @@ if __name__ == '__main__':
...
@@ -56,4 +56,4 @@ if __name__ == '__main__':
# Start app
# Start app
app
.
debug
=
True
app
.
debug
=
True
app
.
run
()
app
.
run
(
'0.0.0.0'
)
flask_adminex/ext/sqlamodel.py
View file @
4049b93a
from
sqlalchemy.orm.properties
import
RelationshipProperty
,
ColumnProperty
from
sqlalchemy.orm.properties
import
RelationshipProperty
,
ColumnProperty
from
sqlalchemy.orm.interfaces
import
MANYTOONE
from
sqlalchemy.orm.interfaces
import
MANYTOONE
from
sqlalchemy.sql.expression
import
desc
from
sqlalchemy
import
exc
from
sqlalchemy
import
exc
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
from
flask
import
request
,
render_template
,
url_for
,
redirect
from
flask
import
request
,
render_template
,
url_for
,
redirect
,
flash
from
flaskext
import
wtf
from
flaskext
import
wtf
...
@@ -56,6 +57,9 @@ class ModelView(BaseView):
...
@@ -56,6 +57,9 @@ class ModelView(BaseView):
edit_template
=
'admin/model/edit.html'
edit_template
=
'admin/model/edit.html'
create_template
=
'admin/model/edit.html'
create_template
=
'admin/model/edit.html'
# Various settings
page_size
=
3
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
# If name not provided, it is modelname
# If name not provided, it is modelname
if
name
is
None
:
if
name
is
None
:
...
@@ -95,7 +99,7 @@ class ModelView(BaseView):
...
@@ -95,7 +99,7 @@ class ModelView(BaseView):
columns
.
append
(
p
.
key
)
columns
.
append
(
p
.
key
)
return
columns
return
[(
c
,
self
.
prettify_name
(
c
))
for
c
in
columns
]
def
scaffold_form
(
self
):
def
scaffold_form
(
self
):
return
model_form
(
self
.
model
,
wtf
.
Form
,
None
,
converter
=
AdminModelConverter
(
self
.
session
))
return
model_form
(
self
.
model
,
wtf
.
Form
,
None
,
converter
=
AdminModelConverter
(
self
.
session
))
...
@@ -107,7 +111,7 @@ class ModelView(BaseView):
...
@@ -107,7 +111,7 @@ class ModelView(BaseView):
return
self
.
scaffold_form
()
return
self
.
scaffold_form
()
# Database-related API
# Database-related API
def
get_
list
(
self
):
def
get_
query
(
self
):
return
self
.
session
.
query
(
self
.
model
)
return
self
.
session
.
query
(
self
.
model
)
def
get_one
(
self
,
id
):
def
get_one
(
self
,
id
):
...
@@ -115,66 +119,151 @@ class ModelView(BaseView):
...
@@ -115,66 +119,151 @@ class ModelView(BaseView):
# Model handlers
# Model handlers
def
create_model
(
self
,
form
):
def
create_model
(
self
,
form
):
# TODO: Error handling
try
:
model
=
self
.
model
()
model
=
self
.
model
()
form
.
populate_obj
(
model
)
form
.
populate_obj
(
model
)
self
.
session
.
add
(
model
)
self
.
session
.
add
(
model
)
self
.
session
.
commit
()
self
.
session
.
commit
()
return
True
except
Exception
,
ex
:
# TODO: Error logging
flash
(
'Failed to create model. '
+
str
(
ex
),
'error'
)
return
False
def
update_model
(
self
,
form
,
model
):
def
update_model
(
self
,
form
,
model
):
form
.
populate_obj
(
model
)
try
:
self
.
session
.
commit
()
form
.
populate_obj
(
model
)
self
.
session
.
commit
()
return
True
except
Exception
,
ex
:
flash
(
'Failed to update model. '
+
str
(
ex
),
'error'
)
return
False
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
self
.
session
.
delete
(
model
)
self
.
session
.
delete
(
model
)
self
.
session
.
commit
()
self
.
session
.
commit
()
# Various helpers
def
prettify_name
(
self
,
name
):
return
' '
.
join
(
x
.
capitalize
()
for
x
in
name
.
split
(
'_'
))
# URL generation helper
def
_get_extra_args
(
self
):
page
=
request
.
args
.
get
(
'page'
,
None
,
type
=
int
)
sort
=
request
.
args
.
get
(
'sort'
,
None
,
type
=
int
)
sort_desc
=
request
.
args
.
get
(
'desc'
,
None
,
type
=
int
)
return
page
,
sort
,
sort_desc
def
_get_url
(
self
,
view
,
page
,
sort
,
sort_desc
):
return
url_for
(
view
,
page
=
page
,
sort
=
sort
,
desc
=
sort_desc
)
# Views
# Views
@
expose
(
'/'
)
@
expose
(
'/'
)
def
index_view
(
self
):
def
index_view
(
self
):
data
=
self
.
get_list
()
data
=
self
.
get_query
()
return
render_template
(
self
.
list_template
,
view
=
self
,
data
=
data
)
page
,
sort
,
sort_desc
=
self
.
_get_extra_args
()
# Sorting
if
sort
is
not
None
:
# Validate first
if
sort
>=
0
and
sort
<
len
(
self
.
list_columns
):
if
sort_desc
:
data
=
data
.
order_by
(
desc
(
self
.
list_columns
[
sort
][
0
]))
else
:
data
=
data
.
order_by
(
self
.
list_columns
[
sort
][
0
])
# Paging
count
=
data
.
count
()
num_pages
=
count
/
self
.
page_size
if
count
%
self
.
page_size
!=
0
:
num_pages
+=
1
if
page
is
not
None
:
if
page
<
1
:
page
=
1
data
=
data
.
offset
((
page
-
1
)
*
self
.
page_size
)
data
=
data
.
limit
(
self
.
page_size
)
# Various URL generation helpers
def
pager_url
(
p
):
return
self
.
_get_url
(
'.index_view'
,
p
,
sort
,
sort_desc
)
def
sort_url
(
column
,
invert
=
False
):
desc
=
None
if
invert
and
not
sort_desc
:
desc
=
1
return
self
.
_get_url
(
'.index_view'
,
page
,
column
,
desc
)
return
render_template
(
self
.
list_template
,
view
=
self
,
data
=
data
,
# Return URL
return_url
=
self
.
_get_url
(
'.index_view'
,
page
,
sort
,
sort_desc
),
# Pagination
pager_url
=
pager_url
,
num_pages
=
num_pages
,
page
=
page
,
# Sorting
sort_column
=
sort
,
sort_desc
=
sort_desc
,
sort_url
=
sort_url
)
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
def
create_view
(
self
):
def
create_view
(
self
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_create
:
if
not
self
.
can_create
:
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
create_form
(
request
.
form
)
form
=
self
.
create_form
(
request
.
form
)
if
form
.
validate_on_submit
():
if
form
.
validate_on_submit
():
self
.
create_model
(
form
)
if
self
.
create_model
(
form
):
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
create_template
,
view
=
self
,
form
=
form
)
return
render_template
(
self
.
create_template
,
view
=
self
,
form
=
form
)
@
expose
(
'/edit/<int:id>/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/edit/<int:id>/'
,
methods
=
(
'GET'
,
'POST'
))
def
edit_view
(
self
,
id
):
def
edit_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_edit
:
if
not
self
.
can_edit
:
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
model
=
self
.
get_one
(
id
)
if
model
is
None
:
if
model
is
None
:
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
form
=
self
.
edit_form
(
request
.
form
,
model
)
form
=
self
.
edit_form
(
request
.
form
,
model
)
if
form
.
validate_on_submit
():
if
form
.
validate_on_submit
():
self
.
update_model
(
form
,
model
)
if
self
.
update_model
(
form
,
model
):
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
return
render_template
(
self
.
edit_template
,
view
=
self
,
form
=
form
)
return
render_template
(
self
.
edit_template
,
view
=
self
,
form
=
form
,
return_url
=
return_url
or
url_for
(
'.index_view'
))
@
expose
(
'/delete/<int:id>/'
)
@
expose
(
'/delete/<int:id>/'
)
def
delete_view
(
self
,
id
):
def
delete_view
(
self
,
id
):
return_url
=
request
.
args
.
get
(
'return'
)
# TODO: Use post
# TODO: Use post
if
not
self
.
can_delete
:
if
not
self
.
can_delete
:
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
model
=
self
.
get_one
(
id
)
model
=
self
.
get_one
(
id
)
if
model
:
if
model
:
self
.
delete_model
(
model
)
self
.
delete_model
(
model
)
return
redirect
(
url_for
(
'.index_view'
))
return
redirect
(
return_url
or
url_for
(
'.index_view'
))
flask_adminex/static/css/admin.css
View file @
4049b93a
/* Body */
body
body
{
{
padding-top
:
50px
;
padding-top
:
50px
;
...
...
flask_adminex/templates/admin/lib.html
0 → 100644
View file @
4049b93a
{% macro pager(page, pages, generator) -%}
{% if pages > 1 %}
<div
class=
"pagination"
>
<ul>
{% if not page %}
{% set page = 1 %}
{% endif %}
{% set min = page - 3 %}
{% set max = page + 3 + 1 %}
{% if min
<
0
%}
{%
set
max =
max
-
min
+
1
%}
{%
endif
%}
{%
if
max
>
pages %}
{% set min = min + pages - max - 1 %}
{% endif %}
{% if min
<
1
%}
{%
set
min =
1
%}
{%
endif
%}
{%
if
max
>
pages + 1 %}
{% set max = pages + 1 %}
{% endif %}
{% if min > 1 %}
<li>
<a
href=
"{{ generator(1) }}"
>
<<
</a>
</li>
{% endif %}
{% if page > 1 %}
<li>
<a
href=
"{{ generator(page-1) }}"
>
<
</a>
</li>
{% endif %}
{% for p in range(min, max) %}
{% if page == p %}
<li
class=
"disabled"
>
<a
href=
"#"
>
{{ p }}
</a>
</li>
{% else %}
<li>
<a
href=
"{{ generator(p) }}"
>
{{ p }}
</a>
</li>
{% endif %}
{% endfor %}
{% if page+1
<
=
pages
%}
<
li
>
<a
href=
"{{ generator(page+1) }}"
>
>
</a>
</li>
{% endif %}
{% if max
<
=
pages
%}
<
li
>
<a
href=
"{{ generator(pages) }}"
>
>>
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{%- endmacro %}
flask_adminex/templates/admin/master.html
View file @
4049b93a
...
@@ -44,6 +44,22 @@
...
@@ -44,6 +44,22 @@
</div>
</div>
</div>
</div>
</div>
</div>
{% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %}
{% for category, m in messages %}
{% if category == 'error' %}
<div
class=
"alert alert-error"
>
{% else %}
<div
class=
"alert"
>
{% endif %}
<a
href=
"#"
class=
"close"
data-dismiss=
"alert"
>
x
</a>
{{ m }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div
class=
"container"
>
<div
class=
"container"
>
{% block body %}{% endblock %}
{% block body %}{% endblock %}
</div>
</div>
...
...
flask_adminex/templates/admin/model/edit.html
View file @
4049b93a
...
@@ -28,6 +28,7 @@
...
@@ -28,6 +28,7 @@
</ul>
</ul>
{% endif %}
{% endif %}
<input
type=
"submit"
class=
"btn btn-primary btn-large"
/>
<input
type=
"submit"
class=
"btn btn-primary btn-large"
/>
<a
href=
"{{ return_url }}"
class=
"btn btn-large"
>
Cancel
</a>
</td>
</td>
</tr>
</tr>
</table>
</table>
...
...
flask_adminex/templates/admin/model/list.html
View file @
4049b93a
{% extends 'admin/master.html' %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib %}
{% block body %}
{% block body %}
<table
class=
"table table-striped table-bordered"
>
<table
class=
"table table-striped table-bordered
model-list
"
>
<thead>
<thead>
<tr>
<tr>
<th></th>
<th
class=
"span1"
>
</th>
{% for c in view.list_columns %}
{% set column = 0 %}
<th>
{{ c }}
</th>
{% for c, name in view.list_columns %}
<th>
{% if sort_column == column %}
<a
href=
"{{ sort_url(column, True) }}"
>
{{ name }}
{% if sort_desc %}
<i
class=
"icon-chevron-up"
></i>
{% else %}
<i
class=
"icon-chevron-down"
></i>
{% endif %}
</a>
{% else %}
<a
href=
"{{ sort_url(column) }}"
>
{{ name }}
</a>
{% endif %}
</th>
{% set column = column + 1 %}
{% endfor %}
{% endfor %}
</tr>
</tr>
</thead>
</thead>
{% for row in data %}
{% for row in data %}
<tr>
<tr>
<td>
<td>
{% if view.can_edit %}
<a
href=
"{{ url_for('.edit_view', id=row.id) }}"
>
Edit
</a>
{% endif %}
{%- if view.can_edit -%}
{% if view.can_delete %}
<a
href=
"{{ url_for('.delete_view', id=row.id) }}"
>
Delete
</a>
{% endif %}
<a
href=
"{{ url_for('.edit_view', id=row.id, return=return_url) }}"
>
<i
class=
"icon-pencil"
></i>
</a>
{%- endif -%}
{%- if view.can_delete -%}
<a
href=
"{{ url_for('.delete_view', id=row.id, return=return_url) }}"
onclick=
"return confirm('You sure you want to delete this item?')"
>
<i
class=
"icon-remove"
></i>
</a>
{%- endif -%}
</td>
</td>
{% for c in view.list_columns %}
{% for c
, name
in view.list_columns %}
<td>
{{ row[c] }}
</td>
<td>
{{ row[c] }}
</td>
{% endfor %}
{% endfor %}
</tr>
</tr>
{% endfor %}
{% endfor %}
</table>
</table>
{{ lib.pager(page, num_pages, pager_url) }}
{% if view.can_create %}
{% if view.can_create %}
<a
class=
"btn btn-primary btn-large"
href=
"{{ url_for('.create_view') }}"
>
Create New
</a>
<a
class=
"btn btn-primary btn-large"
href=
"{{ url_for('.create_view') }}"
>
Create New
</a>
{% endif %}
{% endif %}
...
...
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