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 @@
- Override base URL (/admin/)
- Model Admin
- Ability to sort by fields that are not visible?
- Proper error display
- SQLA Model Admin
- Validation of the joins in the query
- Automatic joined load for foreign keys
- Automatic PK detection
- Filtering
- Many2Many editing
- One2Many editor
- File admin
- Documentation
- Examples
...
...
examples/sqla/simple.py
View file @
37ae5a07
...
...
@@ -37,6 +37,9 @@ class Post(db.Model):
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
def
__unicode__
(
self
):
return
self
.
title
# Flask routes
@
app
.
route
(
'/'
)
...
...
flask_adminex/ext/sqlamodel.py
View file @
37ae5a07
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.sql.expression
import
desc
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
flaskext
import
wtf
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex.form
import
AdminForm
class
AdminModelConverter
(
ModelConverter
):
"""
SQLAlchemy model to form converter
"""
def
__init__
(
self
,
session
):
def
__init__
(
self
,
view
):
super
(
AdminModelConverter
,
self
)
.
__init__
()
self
.
session
=
session
self
.
view
=
view
def
convert
(
self
,
model
,
mapper
,
prop
,
field_args
):
if
isinstance
(
prop
,
RelationshipProperty
):
local_column
=
prop
.
local_remote_pairs
[
0
][
0
]
remote_model
=
prop
.
mapper
.
class_
kwargs
=
{
'validators'
:
[],
'filters'
:
[],
'allow_blank'
:
local_column
.
nullable
,
'default'
:
None
}
if
field_args
:
kwargs
.
update
(
field_args
)
if
prop
.
direction
is
MANYTOONE
:
def
query_factory
():
return
self
.
session
.
query
(
prop
.
argument
)
def
query_factory
():
return
self
.
view
.
session
.
query
(
remote_model
)
if
prop
.
direction
is
MANYTOONE
:
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
:
# Ignore pk/fk
if
isinstance
(
prop
,
ColumnProperty
):
...
...
@@ -58,6 +69,12 @@ class ModelView(BaseModelView):
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
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
"""
...
...
@@ -115,9 +132,14 @@ class ModelView(BaseModelView):
for
p
in
mapper
.
iterate_properties
:
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
]
# Can't sort by on primary and foreign keys by default
if
column
.
foreign_keys
or
column
.
primary_key
:
continue
...
...
@@ -130,10 +152,10 @@ class ModelView(BaseModelView):
Create form from the model.
"""
return
model_form
(
self
.
model
,
wtf
.
Form
,
Admin
Form
,
self
.
form_columns
,
field_args
=
self
.
form_args
,
converter
=
AdminModelConverter
(
self
.
session
))
converter
=
AdminModelConverter
(
self
))
# Database-related API
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):
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.
"""
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 @@
<html>
<head>
<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=
"description"
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-responsive.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/admin.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% block head %}
{% endblock %}
</head>
<body>
{% block page_body %}
...
...
@@ -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=
"{{ 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>
</html>
flask_adminex/templates/admin/model/edit.html
View file @
37ae5a07
{% extends 'admin/master.html' %}
{% block head %}
<link
href=
"{{ url_for('admin.static', filename='chosen/chosen.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% 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>
{{ form.csrf }}
{% for f in form if f.label.text != 'Csrf' %}
{% if f.name in form.errors %}
<div
class=
"control-group error"
>
{% else %}
<div
class=
"control-group"
>
{% endif %}
<div
class=
"control-group{% if f.errors %} error{% endif %}"
>
{{ f.label(class='control-label') }}
<div
class=
"controls"
>
<div>
{{ f }}
</div>
{% if f.
name in form.
errors %}
{% if f.errors %}
<ul>
{% for e in f
orm.errors[f.name]
%}
{% for e in f
.errors
%}
<li>
{{ e }}
</li>
{% endfor %}
</ul>
...
...
@@ -35,3 +35,9 @@
</fieldset>
</form>
{% 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