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
37ae5a07
Commit
37ae5a07
authored
Mar 20, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Multi-select for many-to-one relationships, jQuery Chosen for drop downs, fixes.
parent
52131105
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
473 additions
and
22 deletions
+473
-22
TODO.txt
TODO.txt
+0
-2
simple.py
examples/sqla/simple.py
+3
-0
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+32
-10
form.py
flask_adminex/form.py
+12
-0
model.py
flask_adminex/model.py
+1
-1
chosen-sprite.png
flask_adminex/static/chosen/chosen-sprite.png
+0
-0
chosen.css
flask_adminex/static/chosen/chosen.css
+392
-0
chosen.jquery.min.js
flask_adminex/static/chosen/chosen.jquery.min.js
+10
-0
master.html
flask_adminex/templates/admin/master.html
+9
-1
edit.html
flask_adminex/templates/admin/model/edit.html
+14
-8
No files found.
TODO.txt
View file @
37ae5a07
...
@@ -3,14 +3,12 @@
...
@@ -3,14 +3,12 @@
- Override base URL (/admin/)
- Override base URL (/admin/)
- Model 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
- SQLA Model Admin
- Validation of the joins in the query
- Validation of the joins in the query
- Automatic joined load for foreign keys
- Automatic joined load for foreign keys
- Automatic PK detection
- Automatic PK detection
- Filtering
- Filtering
- Many2Many editing
- Many2Many editing
- One2Many editor
- File admin
- File admin
- Documentation
- Documentation
- Examples
- Examples
...
...
examples/sqla/simple.py
View file @
37ae5a07
...
@@ -37,6 +37,9 @@ class Post(db.Model):
...
@@ -37,6 +37,9 @@ class Post(db.Model):
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
def
__unicode__
(
self
):
return
self
.
title
# Flask routes
# Flask routes
@
app
.
route
(
'/'
)
@
app
.
route
(
'/'
)
...
...
flask_adminex/ext/sqlamodel.py
View file @
37ae5a07
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
,
ONETOMANY
from
sqlalchemy.orm.attributes
import
InstrumentedAttribute
from
sqlalchemy.orm.attributes
import
InstrumentedAttribute
from
sqlalchemy.sql.expression
import
desc
from
sqlalchemy.sql.expression
import
desc
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
,
QuerySelectMultipleField
from
flask
import
flash
from
flask
import
flash
from
flaskext
import
wtf
from
flaskext
import
wtf
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex.form
import
AdminForm
class
AdminModelConverter
(
ModelConverter
):
class
AdminModelConverter
(
ModelConverter
):
"""
"""
SQLAlchemy model to form converter
SQLAlchemy model to form converter
"""
"""
def
__init__
(
self
,
session
):
def
__init__
(
self
,
view
):
super
(
AdminModelConverter
,
self
)
.
__init__
()
super
(
AdminModelConverter
,
self
)
.
__init__
()
self
.
session
=
session
self
.
view
=
view
def
convert
(
self
,
model
,
mapper
,
prop
,
field_args
):
def
convert
(
self
,
model
,
mapper
,
prop
,
field_args
):
if
isinstance
(
prop
,
RelationshipProperty
):
if
isinstance
(
prop
,
RelationshipProperty
):
local_column
=
prop
.
local_remote_pairs
[
0
][
0
]
remote_model
=
prop
.
mapper
.
class_
kwargs
=
{
kwargs
=
{
'validators'
:
[],
'validators'
:
[],
'filters'
:
[],
'filters'
:
[],
'allow_blank'
:
local_column
.
nullable
,
'default'
:
None
'default'
:
None
}
}
if
field_args
:
if
field_args
:
kwargs
.
update
(
field_args
)
kwargs
.
update
(
field_args
)
if
prop
.
direction
is
MANYTOONE
:
def
query_factory
():
def
query_factory
():
return
self
.
view
.
session
.
query
(
remote_model
)
return
self
.
session
.
query
(
prop
.
argument
)
if
prop
.
direction
is
MANYTOONE
:
return
QuerySelectField
(
query_factory
=
query_factory
,
**
kwargs
)
return
QuerySelectField
(
query_factory
=
query_factory
,
**
kwargs
)
elif
prop
.
direction
is
ONETOMANY
:
# Skip backrefs
if
not
local_column
.
foreign_keys
and
self
.
view
.
hide_backrefs
:
return
None
return
QuerySelectMultipleField
(
query_factory
=
query_factory
,
**
kwargs
)
else
:
else
:
# Ignore pk/fk
# Ignore pk/fk
if
isinstance
(
prop
,
ColumnProperty
):
if
isinstance
(
prop
,
ColumnProperty
):
...
@@ -58,6 +69,12 @@ class ModelView(BaseModelView):
...
@@ -58,6 +69,12 @@ class ModelView(BaseModelView):
admin = ModelView(User, db.session)
admin = ModelView(User, db.session)
"""
"""
hide_backrefs
=
True
"""
Set this to False if you want to see multiselect for model backrefs.
"""
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
):
"""
"""
...
@@ -115,9 +132,14 @@ class ModelView(BaseModelView):
...
@@ -115,9 +132,14 @@ class ModelView(BaseModelView):
for
p
in
mapper
.
iterate_properties
:
for
p
in
mapper
.
iterate_properties
:
if
isinstance
(
p
,
ColumnProperty
):
if
isinstance
(
p
,
ColumnProperty
):
# TODO: Check for multiple columns
# Sanity check
if
len
(
p
.
columns
)
>
1
:
raise
Exception
(
'Automatic form scaffolding is not supported'
+
' for multi-column properties (
%
s.
%
s)'
%
(
self
.
model
.
__name__
,
p
.
key
))
column
=
p
.
columns
[
0
]
column
=
p
.
columns
[
0
]
# Can't sort by on primary and foreign keys by default
if
column
.
foreign_keys
or
column
.
primary_key
:
if
column
.
foreign_keys
or
column
.
primary_key
:
continue
continue
...
@@ -130,10 +152,10 @@ class ModelView(BaseModelView):
...
@@ -130,10 +152,10 @@ class ModelView(BaseModelView):
Create form from the model.
Create form from the model.
"""
"""
return
model_form
(
self
.
model
,
return
model_form
(
self
.
model
,
wtf
.
Form
,
Admin
Form
,
self
.
form_columns
,
self
.
form_columns
,
field_args
=
self
.
form_args
,
field_args
=
self
.
form_args
,
converter
=
AdminModelConverter
(
self
.
session
))
converter
=
AdminModelConverter
(
self
))
# Database-related API
# Database-related API
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
,
execute
=
True
):
def
get_list
(
self
,
page
,
sort_column
,
sort_desc
,
execute
=
True
):
...
...
flask_adminex/form.py
0 → 100644
View file @
37ae5a07
from
flask.ext
import
wtf
class
AdminForm
(
wtf
.
Form
):
@
property
def
has_file_field
(
self
):
# TODO: Optimize me
for
f
in
self
:
if
isinstance
(
f
,
wtf
.
FileField
):
return
True
return
False
flask_adminex/model.py
View file @
37ae5a07
...
@@ -220,7 +220,7 @@ class BaseModelView(BaseView):
...
@@ -220,7 +220,7 @@ class BaseModelView(BaseView):
def
scaffold_form
(
self
):
def
scaffold_form
(
self
):
"""
"""
Create
WTForm
class from the model. Must be implemented in
Create
`form.AdminForm`
class from the model. Must be implemented in
the child class.
the child class.
"""
"""
raise
NotImplemented
(
'Please implement scaffold_form method'
)
raise
NotImplemented
(
'Please implement scaffold_form method'
)
...
...
flask_adminex/static/chosen/chosen-sprite.png
0 → 100644
View file @
37ae5a07
559 Bytes
flask_adminex/static/chosen/chosen.css
0 → 100644
View file @
37ae5a07
This diff is collapsed.
Click to expand it.
flask_adminex/static/chosen/chosen.jquery.min.js
0 → 100644
View file @
37ae5a07
This diff is collapsed.
Click to expand it.
flask_adminex/templates/admin/master.html
View file @
37ae5a07
...
@@ -2,14 +2,18 @@
...
@@ -2,14 +2,18 @@
<html>
<html>
<head>
<head>
<title>
{% block title %}{% if view.category %}{{ view.category }} - {% endif %}{{ view.name }} - {{ view.admin.name }}{% endblock %}
</title>
<title>
{% block title %}{% if view.category %}{{ view.category }} - {% endif %}{{ view.name }} - {{ view.admin.name }}{% endblock %}
</title>
{% block head %}
{% block head
_meta
%}
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"description"
content=
""
>
<meta
name=
"description"
content=
""
>
<meta
name=
"author"
content=
""
>
<meta
name=
"author"
content=
""
>
{% endblock %}
{% block head_css %}
<link
href=
"{{ url_for('admin.static', filename='bootstrap/css/bootstrap.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='bootstrap/css/bootstrap.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='bootstrap/css/bootstrap-responsive.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='bootstrap/css/bootstrap-responsive.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/admin.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/admin.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% endblock %}
{% block head %}
{% endblock %}
</head>
</head>
<body>
<body>
{% block page_body %}
{% block page_body %}
...
@@ -68,5 +72,9 @@
...
@@ -68,5 +72,9 @@
<script
src=
"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"
type=
"text/javascript"
></script>
<script
src=
"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"
type=
"text/javascript"
></script>
<script
src=
"{{ url_for('admin.static', filename='bootstrap/js/bootstrap.min.js') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ url_for('admin.static', filename='bootstrap/js/bootstrap.min.js') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ url_for('admin.static', filename='chosen/chosen.jquery.min.js') }}"
type=
"text/javascript"
></script>
{% block tail %}
{% endblock %}
</body>
</body>
</html>
</html>
flask_adminex/templates/admin/model/edit.html
View file @
37ae5a07
{% extends 'admin/master.html' %}
{% extends 'admin/master.html' %}
{% block head %}
<link
href=
"{{ url_for('admin.static', filename='chosen/chosen.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% block body %}
{% block body %}
<form
action=
""
method=
"POST"
class=
"form-horizontal"
>
<form
action=
""
method=
"POST"
class=
"form-horizontal"
{%
if
form
.
has_file_field
%}
enctype=
"multipart/form-data"
{%
endif
%}
>
<fieldset>
<fieldset>
{{ form.csrf }}
{{ form.csrf }}
{% for f in form if f.label.text != 'Csrf' %}
{% for f in form if f.label.text != 'Csrf' %}
{% if f.name in form.errors %}
<div
class=
"control-group{% if f.errors %} error{% endif %}"
>
<div
class=
"control-group error"
>
{% else %}
<div
class=
"control-group"
>
{% endif %}
{{ f.label(class='control-label') }}
{{ f.label(class='control-label') }}
<div
class=
"controls"
>
<div
class=
"controls"
>
<div>
<div>
{{ f }}
{{ f }}
</div>
</div>
{% if f.
name in form.
errors %}
{% if f.errors %}
<ul>
<ul>
{% for e in f
orm.errors[f.name]
%}
{% for e in f
.errors
%}
<li>
{{ e }}
</li>
<li>
{{ e }}
</li>
{% endfor %}
{% endfor %}
</ul>
</ul>
...
@@ -35,3 +35,9 @@
...
@@ -35,3 +35,9 @@
</fieldset>
</fieldset>
</form>
</form>
{% endblock %}
{% endblock %}
{% block tail %}
<script>
$
(
"select"
).
chosen
({
allow_single_deselect
:
true
});
</script>
{% 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