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
52131105
Commit
52131105
authored
Mar 20, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Restyled edit form, more documentation.
parent
80b8377d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
196 additions
and
48 deletions
+196
-48
TODO.txt
TODO.txt
+2
-2
simple.py
examples/sqla/simple.py
+5
-1
base.py
flask_adminex/base.py
+19
-2
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+75
-1
model.py
flask_adminex/model.py
+23
-1
edit.html
flask_adminex/templates/admin/model/edit.html
+30
-29
tools.py
flask_adminex/tools.py
+42
-12
No files found.
TODO.txt
View file @
52131105
...
...
@@ -2,9 +2,9 @@
- Pregenerate URLs for menu
- Override base URL (/admin/)
- Model Admin
Ability to sort by fields that are not visible?
- Ability to sort by fields that are not visible?
- Proper error display
- SQLA Model Admin
- Sort by foreign key
- Validation of the joins in the query
- Automatic joined load for foreign keys
- Automatic PK detection
...
...
examples/sqla/simple.py
View file @
52131105
from
flask
import
Flask
from
flaskext.sqlalchemy
import
SQLAlchemy
from
flask.ext
import
adminex
from
flask.ext
import
adminex
,
wtf
from
flask.ext.adminex.ext
import
sqlamodel
# Create application
...
...
@@ -52,6 +52,10 @@ class PostAdmin(sqlamodel.ModelView):
sortable_columns
=
(
'title'
,
(
'user'
,
User
.
username
))
rename_columns
=
dict
(
title
=
'Tiiitle'
)
form_args
=
dict
(
text
=
dict
(
label
=
'Big Text'
,
validators
=
[
wtf
.
required
()])
)
def
__init__
(
self
,
session
):
super
(
PostAdmin
,
self
)
.
__init__
(
Post
,
session
)
...
...
flask_adminex/base.py
View file @
52131105
...
...
@@ -84,6 +84,7 @@ class BaseView(object):
def
__init__
(
self
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
,
static_folder
=
None
):
"""
Constructor.
`name`
Name of this view. If not provided, will be defaulted to the class name.
`category`
...
...
@@ -111,6 +112,9 @@ class BaseView(object):
def
_set_admin
(
self
,
admin
):
"""
Associate this view with Admin class instance.
`admin`
Admin instance
"""
self
.
admin
=
admin
...
...
@@ -145,6 +149,9 @@ class BaseView(object):
def
_prettify_name
(
self
,
name
):
"""
Prettify class name by splitting name by capital characters. So, 'MySuperClass' will look like 'My Super Class'
`name`
String to prettify
"""
return
sub
(
r'(?<=.)([A-Z])'
,
r' \1'
,
name
)
...
...
@@ -176,6 +183,12 @@ class AdminIndexView(BaseView):
return render_template('adminhome.html')
admin = Admin(index_view=MyHomeView)
By default, has following rules:
1. If name is not provided, will use 'Home'
2. If endpoint is not provided, will use 'admin'
3. If url is not provided, will use '/admin'
4. Automatically associates with static folder.
"""
def
__init__
(
self
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
super
(
AdminIndexView
,
self
)
.
__init__
(
name
or
'Home'
,
category
,
endpoint
or
'admin'
,
url
or
'/admin'
,
'static'
)
...
...
@@ -186,6 +199,9 @@ class AdminIndexView(BaseView):
class
MenuItem
(
object
):
"""
Simple menu tree hierarchy.
"""
def
__init__
(
self
,
name
,
view
=
None
):
self
.
name
=
name
self
.
_view
=
view
...
...
@@ -204,6 +220,7 @@ class MenuItem(object):
if
self
.
_view
is
None
:
return
None
# TODO: Optimize me
return
url_for
(
'
%
s.
%
s'
%
(
self
.
_view
.
endpoint
,
self
.
_view
.
_default_view
))
def
is_active
(
self
,
view
):
...
...
@@ -237,7 +254,7 @@ class Admin(object):
Constructor.
`name`
Application name. Will be displayed in main menu and as a page title. If not provided, defaulted to "
Flask
"
Application name. Will be displayed in main menu and as a page title. If not provided, defaulted to "
Admin
"
`index_view`
Home page view to use. If not provided, will use `AdminIndexView`.
"""
...
...
@@ -245,7 +262,7 @@ class Admin(object):
self
.
_menu
=
[]
if
name
is
None
:
name
=
'
Flask
'
name
=
'
Admin
'
self
.
name
=
name
if
index_view
is
None
:
...
...
flask_adminex/ext/sqlamodel.py
View file @
52131105
...
...
@@ -14,6 +14,9 @@ from flask.ext.adminex.model import BaseModelView
class
AdminModelConverter
(
ModelConverter
):
"""
SQLAlchemy model to form converter
"""
def
__init__
(
self
,
session
):
super
(
AdminModelConverter
,
self
)
.
__init__
()
...
...
@@ -48,14 +51,40 @@ class AdminModelConverter(ModelConverter):
class
ModelView
(
BaseModelView
):
"""
SQLALchemy model view
Usage sample::
admin = ModelView(User, db.session)
"""
def
__init__
(
self
,
model
,
session
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
"""
Constructor.
`model`
Model class
`session`
SQLALchemy session
`name`
View name. If not set, will default to model name
`category`
Category name
`endpoint`
Endpoint name. If not set, will default to model name
`url`
Base URL. If not set, will default to '/admin/' + endpoint
"""
self
.
session
=
session
super
(
ModelView
,
self
)
.
__init__
(
model
,
name
,
category
,
endpoint
,
url
)
#
Public API
#
Scaffolding
def
scaffold_list_columns
(
self
):
"""
Return list of columns from the model.
"""
columns
=
[]
mapper
=
self
.
model
.
_sa_class_manager
.
mapper
...
...
@@ -76,6 +105,10 @@ class ModelView(BaseModelView):
return
columns
def
scaffold_sortable_columns
(
self
):
"""
Return dictionary of sortable columns.
Key is column name, value is sort column/field.
"""
columns
=
dict
()
mapper
=
self
.
model
.
_sa_class_manager
.
mapper
...
...
@@ -93,13 +126,29 @@ class ModelView(BaseModelView):
return
columns
def
scaffold_form
(
self
):
"""
Create form from the model.
"""
return
model_form
(
self
.
model
,
wtf
.
Form
,
self
.
form_columns
,
field_args
=
self
.
form_args
,
converter
=
AdminModelConverter
(
self
.
session
))
# Database-related API
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
,
execute
=
True
):
"""
Return models from the database.
`page`
Page number
`sort_column`
Sort column name
`sort_desc`
Descending or ascending sort
`execute`
Execute query immediately? Default is `True`
"""
query
=
self
.
session
.
query
(
self
.
model
)
count
=
query
.
count
()
...
...
@@ -133,16 +182,29 @@ class ModelView(BaseModelView):
query
=
query
.
limit
(
self
.
page_size
)
# Execute if needed
if
execute
:
query
=
query
.
all
()
return
count
,
query
def
get_one
(
self
,
id
):
"""
Return one model by its id.
`id`
Model
"""
return
self
.
session
.
query
(
self
.
model
)
.
get
(
id
)
# Model handlers
def
create_model
(
self
,
form
):
"""
Create model from form.
`form`
Form instance
"""
try
:
model
=
self
.
model
()
form
.
populate_obj
(
model
)
...
...
@@ -154,6 +216,12 @@ class ModelView(BaseModelView):
return
False
def
update_model
(
self
,
form
,
model
):
"""
Update model from form.
`form`
Form instance
"""
try
:
form
.
populate_obj
(
model
)
self
.
session
.
commit
()
...
...
@@ -163,5 +231,11 @@ class ModelView(BaseModelView):
return
False
def
delete_model
(
self
,
model
):
"""
Delete model.
`model`
Model to delete
"""
self
.
session
.
delete
(
model
)
self
.
session
.
commit
()
flask_adminex/model.py
View file @
52131105
...
...
@@ -96,6 +96,19 @@ class BaseModelView(BaseView):
list_columns = ('name', 'email')
"""
form_args
=
None
"""
Dictionary of form field arguments. Refer to WTForm documentation on
list of possible options.
Example::
class MyModelView(BaseModelView):
form_args = dict(
name=dict(label='First Name', validators=[wtf.required()])
}
"""
# Various settings
page_size
=
20
"""
...
...
@@ -343,7 +356,7 @@ class BaseModelView(BaseView):
`name`
Name to prettify
"""
return
' '
.
join
(
x
.
capitalize
()
for
x
in
name
.
split
(
'_'
)
)
return
name
.
replace
(
'_'
,
' '
)
.
title
(
)
# URL generation helper
def
_get_extra_args
(
self
):
...
...
@@ -434,6 +447,9 @@ class BaseModelView(BaseView):
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
def
create_view
(
self
):
"""
Create model view
"""
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_create
:
...
...
@@ -449,6 +465,9 @@ class BaseModelView(BaseView):
@
expose
(
'/edit/<int:id>/'
,
methods
=
(
'GET'
,
'POST'
))
def
edit_view
(
self
,
id
):
"""
Edit model view
"""
return_url
=
request
.
args
.
get
(
'return'
)
if
not
self
.
can_edit
:
...
...
@@ -472,6 +491,9 @@ class BaseModelView(BaseView):
@
expose
(
'/delete/<int:id>/'
)
def
delete_view
(
self
,
id
):
"""
Delete model view
"""
return_url
=
request
.
args
.
get
(
'return'
)
# TODO: Use post
...
...
flask_adminex/templates/admin/model/edit.html
View file @
52131105
{% extends 'admin/master.html' %}
{% block body %}
<form
action=
""
method=
"POST"
>
{{ form.csrf }}
<table
class=
"form"
>
<form
action=
""
method=
"POST"
class=
"form-horizontal"
>
<fieldset>
{{ form.csrf }}
{% for f in form if f.label.text != 'Csrf' %}
<tr>
<td>
{{ f.label }}
</td>
<td>
{{ f }}
</td>
</tr>
{% if f.name in form.errors %}
<div
class=
"control-group error"
>
{% else %}
<div
class=
"control-group"
>
{% endif %}
{{ f.label(class='control-label') }}
<div
class=
"controls"
>
<div>
{{ f }}
</div>
{% if f.name in form.errors %}
<ul>
{% for e in form.errors[f.name] %}
<li>
{{ e }}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endfor %}
<tr>
<td>
</td>
<td>
{% if form.errors %}
<ul>
{% for fn, fe in form.errors.items() if fe %}
{% for e in fe %}
<li>
{{ e }}
</li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
<input
type=
"submit"
class=
"btn btn-primary btn-large"
/>
<a
href=
"{{ return_url }}"
class=
"btn btn-large"
>
Cancel
</a>
</td>
</tr>
</table>
<div
class=
"control-group"
>
<div
class=
"controls"
>
<input
type=
"submit"
class=
"btn btn-primary btn-large"
/>
<a
href=
"{{ return_url }}"
class=
"btn btn-large"
>
Cancel
</a>
</div>
</div>
</fieldset>
</form>
{% endblock %}
flask_adminex/tools.py
View file @
52131105
...
...
@@ -2,6 +2,16 @@ import sys, traceback
def
import_module
(
name
,
required
=
True
):
"""
Import module by name
`name`
Module name
`required`
If set to `True` and module was not found - will throw exception.
If set to `False` and module was not found - will return None.
Default is `True`.
"""
try
:
__import__
(
name
,
globals
(),
locals
(),
[])
except
ImportError
:
...
...
@@ -13,10 +23,17 @@ def import_module(name, required=True):
def
import_attribute
(
name
):
"""
Import attribute using string reference.
Example:
import_attribute('a.b.c.foo')
Throws ImportError or AttributeError if module or attribute do not exist.
Import attribute using string reference.
`name`
String reference.
Throws ImportError or AttributeError if module or attribute do not exist.
Example::
import_attribute('a.b.c.foo')
"""
path
,
attr
=
name
.
rsplit
(
'.'
,
1
)
module
=
__import__
(
path
,
globals
(),
locals
(),
[
attr
])
...
...
@@ -25,13 +42,15 @@ def import_attribute(name):
def
module_not_found
(
additional_depth
=
0
):
'''Checks if ImportError was raised because module does not exist or
something inside it raised ImportError
"""
Checks if ImportError was raised because module does not exist or
something inside it raised ImportError
- additional_depth - supply int of depth of your call if you're not doing
import on the same level of code - f.e., if you call function, which is
doing import, you should pass 1 for single additional level of depth
'''
`additional_depth`
supply int of depth of your call if you're not doing
import on the same level of code - f.e., if you call function, which is
doing import, you should pass 1 for single additional level of depth
"""
tb
=
sys
.
exc_info
()[
2
]
if
len
(
traceback
.
extract_tb
(
tb
))
>
(
1
+
additional_depth
):
return
False
...
...
@@ -39,8 +58,19 @@ def module_not_found(additional_depth=0):
def
rec_getattr
(
obj
,
attr
,
default
=
None
):
"""
Recursive getattr.
`attr`
Dot delimited attribute name
`default`
Default value
Example::
rec_getattr(obj, 'a.b.c')
"""
try
:
return
reduce
(
getattr
,
attr
.
split
(
'.'
),
obj
)
except
AttributeError
:
return
None
return
default
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