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
48ebb0fc
Commit
48ebb0fc
authored
Dec 25, 2013
by
bryhoyt
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4 from mrjoes/master
Merge latest main repo into my local
parents
ee750e5c
9a87a0be
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
111 additions
and
44 deletions
+111
-44
sidebarintro.html
doc/_templates/sidebarintro.html
+1
-1
form.html
examples/auth-mongoengine/templates/form.html
+1
-1
simple.py
examples/layout/simple.py
+1
-0
fileadmin.py
flask_admin/contrib/fileadmin.py
+35
-10
filters.py
flask_admin/contrib/mongoengine/filters.py
+1
-1
subdoc.py
flask_admin/contrib/mongoengine/subdoc.py
+0
-8
fields.py
flask_admin/contrib/sqla/fields.py
+13
-2
tools.py
flask_admin/contrib/sqla/tools.py
+1
-1
form.py
flask_admin/model/form.py
+9
-1
filters.js
flask_admin/static/admin/js/filters.js
+1
-1
list.html
flask_admin/templates/admin/file/list.html
+4
-0
lib.html
flask_admin/templates/admin/lib.html
+7
-6
inline_list_base.html
flask_admin/templates/admin/model/inline_list_base.html
+10
-4
list.html
flask_admin/templates/admin/model/list.html
+8
-8
test_form_rules.py
flask_admin/tests/sqlamodel/test_form_rules.py
+19
-0
No files found.
doc/_templates/sidebarintro.html
View file @
48ebb0fc
...
@@ -5,4 +5,4 @@
...
@@ -5,4 +5,4 @@
</ul>
</ul>
<a
href=
"http://github.com/mrjoes/flask-admin"
><img
style=
"position: fixed; top: 0; right: 0; border: 0;"
<a
href=
"http://github.com/mrjoes/flask-admin"
><img
style=
"position: fixed; top: 0; right: 0; border: 0;"
src=
"
http:
//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"
alt=
"Fork me on GitHub"
/></a>
src=
"//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"
alt=
"Fork me on GitHub"
/></a>
examples/auth-mongoengine/templates/form.html
View file @
48ebb0fc
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
{{ f }}
{{ f }}
{% if f.errors %}
{% if f.errors %}
<ul>
<ul>
{% for e in f.err
r
ors %}
{% for e in f.errors %}
<li>
{{ e }}
</li>
<li>
{{ e }}
</li>
{% endfor %}
{% endfor %}
</ul>
</ul>
...
...
examples/layout/simple.py
View file @
48ebb0fc
import
os
import
os
import
os.path
as
op
from
flask
import
Flask
from
flask
import
Flask
from
flask.ext.sqlalchemy
import
SQLAlchemy
from
flask.ext.sqlalchemy
import
SQLAlchemy
...
...
flask_admin/contrib/fileadmin.py
View file @
48ebb0fc
...
@@ -7,7 +7,7 @@ import shutil
...
@@ -7,7 +7,7 @@ import shutil
from
operator
import
itemgetter
from
operator
import
itemgetter
from
werkzeug
import
secure_filename
from
werkzeug
import
secure_filename
from
flask
import
flash
,
url_for
,
redirect
,
abort
,
request
from
flask
import
flash
,
url_for
,
redirect
,
abort
,
request
,
send_file
from
wtforms
import
fields
,
validators
from
wtforms
import
fields
,
validators
...
@@ -64,13 +64,12 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -64,13 +64,12 @@ class FileAdmin(BaseView, ActionsMixin):
"""
"""
Simple file-management interface.
Simple file-management interface.
Requires two parameters:
:param path:
:param path:
Path to the directory which will be managed
Path to the directory which will be managed
:param url:
:param base_url:
Base URL for the directory. Will be used to generate
Optional base URL for the directory. Will be used to generate
static links to the files.
static links to the files. If not defined, a route will be created
to serve uploaded files.
Sample usage::
Sample usage::
...
@@ -86,6 +85,11 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -86,6 +85,11 @@ class FileAdmin(BaseView, ActionsMixin):
Is file upload allowed.
Is file upload allowed.
"""
"""
can_download
=
True
"""
Is file download allowed.
"""
can_delete
=
True
can_delete
=
True
"""
"""
Is file deletion allowed.
Is file deletion allowed.
...
@@ -151,7 +155,7 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -151,7 +155,7 @@ class FileAdmin(BaseView, ActionsMixin):
Edit template
Edit template
"""
"""
def
__init__
(
self
,
base_path
,
base_url
,
def
__init__
(
self
,
base_path
,
base_url
=
None
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
,
verify_path
=
True
):
verify_path
=
True
):
"""
"""
...
@@ -310,10 +314,10 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -310,10 +314,10 @@ class FileAdmin(BaseView, ActionsMixin):
Static file path
Static file path
"""
"""
if
self
.
is_file_editable
(
path
):
if
self
.
is_file_editable
(
path
):
r
eturn
url_for
(
".edit"
,
path
=
path
)
r
oute
=
'.edit'
else
:
else
:
base_url
=
self
.
get_base_url
()
route
=
'.download'
return
urljoin
(
base_url
,
path
)
return
url_for
(
route
,
path
=
path
)
def
_normalize_path
(
self
,
path
):
def
_normalize_path
(
self
,
path
):
"""
"""
...
@@ -503,6 +507,27 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -503,6 +507,27 @@ class FileAdmin(BaseView, ActionsMixin):
return
self
.
render
(
self
.
upload_template
,
form
=
form
)
return
self
.
render
(
self
.
upload_template
,
form
=
form
)
@
expose
(
'/download/<path:path>'
)
def
download
(
self
,
path
=
None
):
"""
Download view method.
:param path:
File path.
"""
if
not
self
.
can_download
:
abort
(
404
)
base_path
,
directory
,
path
=
self
.
_normalize_path
(
path
)
# backward compatibility with base_url
base_url
=
self
.
get_base_url
()
if
base_url
:
base_url
=
urljoin
(
url_for
(
'.index'
),
base_url
)
return
redirect
(
urljoin
(
base_url
,
path
))
return
send_file
(
directory
)
@
expose
(
'/mkdir/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/mkdir/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/mkdir/<path:path>'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/mkdir/<path:path>'
,
methods
=
(
'GET'
,
'POST'
))
def
mkdir
(
self
,
path
=
None
):
def
mkdir
(
self
,
path
=
None
):
...
...
flask_admin/contrib/mongoengine/filters.py
View file @
48ebb0fc
...
@@ -107,7 +107,7 @@ class FilterConverter(filters.BaseFilterConverter):
...
@@ -107,7 +107,7 @@ class FilterConverter(filters.BaseFilterConverter):
return
None
return
None
@
filters
.
convert
(
'StringField'
)
@
filters
.
convert
(
'StringField'
,
'EmailField'
)
def
conv_string
(
self
,
column
,
name
):
def
conv_string
(
self
,
column
,
name
):
return
[
f
(
column
,
name
)
for
f
in
self
.
strings
]
return
[
f
(
column
,
name
)
for
f
in
self
.
strings
]
...
...
flask_admin/contrib/mongoengine/subdoc.py
View file @
48ebb0fc
from
flask.ext.admin._compat
import
iteritems
from
flask.ext.admin._compat
import
iteritems
from
flask.ext.admin.form
import
rules
from
flask.ext.admin.model.form
import
InlineBaseFormAdmin
from
flask.ext.admin.model.form
import
InlineBaseFormAdmin
...
@@ -9,13 +8,6 @@ class EmbeddedForm(InlineBaseFormAdmin):
...
@@ -9,13 +8,6 @@ class EmbeddedForm(InlineBaseFormAdmin):
self
.
_form_subdocuments
=
convert_subdocuments
(
getattr
(
self
,
'form_subdocuments'
,
{}))
self
.
_form_subdocuments
=
convert_subdocuments
(
getattr
(
self
,
'form_subdocuments'
,
{}))
form_rules
=
getattr
(
self
,
'form_rules'
,
None
)
if
form_rules
:
self
.
_form_rules
=
rules
.
RuleSet
(
self
,
form_rules
)
else
:
self
.
_form_rules
=
None
def
convert_subdocuments
(
values
):
def
convert_subdocuments
(
values
):
result
=
{}
result
=
{}
...
...
flask_admin/contrib/sqla/fields.py
View file @
48ebb0fc
...
@@ -9,7 +9,9 @@ from wtforms.validators import ValidationError
...
@@ -9,7 +9,9 @@ from wtforms.validators import ValidationError
from
.tools
import
get_primary_key
from
.tools
import
get_primary_key
from
flask.ext.admin._compat
import
text_type
,
string_types
from
flask.ext.admin._compat
import
text_type
,
string_types
from
flask.ext.admin.form
import
FormOpts
from
flask.ext.admin.model.fields
import
InlineFieldList
,
InlineModelFormField
from
flask.ext.admin.model.fields
import
InlineFieldList
,
InlineModelFormField
from
flask.ext.admin.model.widgets
import
InlineFormWidget
try
:
try
:
...
@@ -186,7 +188,7 @@ class InlineModelFormList(InlineFieldList):
...
@@ -186,7 +188,7 @@ class InlineModelFormList(InlineFieldList):
Form field type. Override to use custom field for each inline form
Form field type. Override to use custom field for each inline form
"""
"""
def
__init__
(
self
,
form
,
session
,
model
,
prop
,
inline_view
,
**
kwargs
):
def
__init__
(
self
,
form
,
session
,
model
,
prop
,
inline_view
,
form_widget
=
None
,
**
kwargs
):
"""
"""
Default constructor.
Default constructor.
...
@@ -209,7 +211,16 @@ class InlineModelFormList(InlineFieldList):
...
@@ -209,7 +211,16 @@ class InlineModelFormList(InlineFieldList):
self
.
_pk
=
get_primary_key
(
model
)
self
.
_pk
=
get_primary_key
(
model
)
super
(
InlineModelFormList
,
self
)
.
__init__
(
self
.
form_field_type
(
form
,
self
.
_pk
),
**
kwargs
)
# Generate inline form field
if
form_widget
is
None
:
form_opts
=
FormOpts
(
widget_args
=
getattr
(
inline_view
,
'form_widget_args'
,
None
),
form_rules
=
inline_view
.
_form_rules
)
form_widget
=
InlineFormWidget
(
form_opts
)
form_field
=
self
.
form_field_type
(
form
,
self
.
_pk
,
widget
=
form_widget
)
super
(
InlineModelFormList
,
self
)
.
__init__
(
form_field
,
**
kwargs
)
def
display_row_controls
(
self
,
field
):
def
display_row_controls
(
self
,
field
):
return
field
.
get_pk
()
is
not
None
return
field
.
get_pk
()
is
not
None
...
...
flask_admin/contrib/sqla/tools.py
View file @
48ebb0fc
...
@@ -31,7 +31,7 @@ def get_primary_key(model):
...
@@ -31,7 +31,7 @@ def get_primary_key(model):
if
is_inherited_primary_key
(
p
):
if
is_inherited_primary_key
(
p
):
pks
.
append
(
get_column_for_current_model
(
p
)
.
key
)
pks
.
append
(
get_column_for_current_model
(
p
)
.
key
)
else
:
else
:
pks
.
append
(
p
.
columns
[
0
]
.
key
)
pks
.
append
(
p
.
key
)
if
len
(
pks
)
==
1
:
if
len
(
pks
)
==
1
:
return
pks
[
0
]
return
pks
[
0
]
elif
len
(
pks
)
>
1
:
elif
len
(
pks
)
>
1
:
...
...
flask_admin/model/form.py
View file @
48ebb0fc
import
inspect
import
inspect
from
flask.ext.admin.form
import
BaseForm
from
flask.ext.admin.form
import
BaseForm
,
rules
from
flask.ext.admin._compat
import
iteritems
from
flask.ext.admin._compat
import
iteritems
...
@@ -37,6 +37,14 @@ class InlineBaseFormAdmin(object):
...
@@ -37,6 +37,14 @@ class InlineBaseFormAdmin(object):
for
k
,
v
in
iteritems
(
kwargs
):
for
k
,
v
in
iteritems
(
kwargs
):
setattr
(
self
,
k
,
v
)
setattr
(
self
,
k
,
v
)
# Convert form rules
form_rules
=
getattr
(
self
,
'form_rules'
,
None
)
if
form_rules
:
self
.
_form_rules
=
rules
.
RuleSet
(
self
,
form_rules
)
else
:
self
.
_form_rules
=
None
def
get_form
(
self
):
def
get_form
(
self
):
"""
"""
If you want to use completely custom form for inline field, you can override
If you want to use completely custom form for inline field, you can override
...
...
flask_admin/static/admin/js/filters.js
View file @
48ebb0fc
...
@@ -10,7 +10,7 @@ var AdminFilters = function(element, filters_element, filters_by_group) {
...
@@ -10,7 +10,7 @@ var AdminFilters = function(element, filters_element, filters_by_group) {
function
changeOperation
()
{
function
changeOperation
()
{
var
$row
=
$
(
this
).
closest
(
'tr'
);
var
$row
=
$
(
this
).
closest
(
'tr'
);
var
$el
=
$
(
'.filter-val'
,
$row
);
var
$el
=
$
(
'.filter-val
:input
'
,
$row
);
var
count
=
getCount
(
$el
.
attr
(
'name'
));
var
count
=
getCount
(
$el
.
attr
(
'name'
));
$el
.
attr
(
'name'
,
'flt'
+
count
+
'_'
+
$
(
this
).
val
());
$el
.
attr
(
'name'
,
'flt'
+
count
+
'_'
+
$
(
this
).
val
());
$
(
'button'
,
$root
).
show
();
$
(
'button'
,
$root
).
show
();
...
...
flask_admin/templates/admin/file/list.html
View file @
48ebb0fc
...
@@ -83,7 +83,11 @@
...
@@ -83,7 +83,11 @@
</td>
</td>
{% else %}
{% else %}
<td>
<td>
{% if admin_view.can_download %}
<a
href=
"{{ get_file_url(path)|safe }}"
>
{{ name }}
</a>
<a
href=
"{{ get_file_url(path)|safe }}"
>
{{ name }}
</a>
{% else %}
{{ name }}
{% endif %}
</td>
</td>
<td>
<td>
{{ size }}
{{ size }}
...
...
flask_admin/templates/admin/lib.html
View file @
48ebb0fc
...
@@ -79,12 +79,13 @@
...
@@ -79,12 +79,13 @@
{% set direct_error = h.is_field_error(field.errors) %}
{% set direct_error = h.is_field_error(field.errors) %}
<div
class=
"control-group{{ ' error' if direct_error else '' }}"
>
<div
class=
"control-group{{ ' error' if direct_error else '' }}"
>
<div
class=
"control-label"
>
<div
class=
"control-label"
>
{{ field.label.text }}
<label
for=
"{{ field.id }}"
>
{{ field.label.text }}
{% if h.is_required_form_field(field) %}
{% if h.is_required_form_field(field) %}
<strong
style=
"color: red"
>
*
</strong>
<strong
style=
"color: red"
>
*
</strong>
{% else %}
{%- else -%}
{% endif %}
{%- endif %}
</label>
</div>
</div>
<div
class=
"controls"
>
<div
class=
"controls"
>
<div>
<div>
...
...
flask_admin/templates/admin/model/inline_list_base.html
View file @
48ebb0fc
...
@@ -4,10 +4,16 @@
...
@@ -4,10 +4,16 @@
{% for subfield in field %}
{% for subfield in field %}
<div
id=
"{{ subfield.id }}"
class=
"fa-inline-field"
>
<div
id=
"{{ subfield.id }}"
class=
"fa-inline-field"
>
{%- if not check or check(subfield) %}
{%- if not check or check(subfield) %}
<div
class=
"fa-inline-field-control"
>
{% if subfield.get_pk and subfield.get_pk() %}
<input
type=
"checkbox"
name=
"del-{{ subfield.id }}"
id=
"del-{{ subfield.id }}"
/>
<div
class=
"fa-inline-field-control"
>
<label
for=
"del-{{ subfield.id }}"
style=
"display: inline"
>
{{ _gettext('Delete?') }}
</label>
<input
type=
"checkbox"
name=
"del-{{ subfield.id }}"
id=
"del-{{ subfield.id }}"
/>
</div>
<label
for=
"del-{{ subfield.id }}"
style=
"display: inline"
>
{{ _gettext('Delete?') }}
</label>
</div>
{% else %}
<div
class=
"fa-inline-field-control"
>
<a
href=
"javascript:void(0)"
class=
"fa-remove-field"
><i
class=
"icon-remove"
></i></a>
</div>
{% endif %}
{%- endif -%}
{%- endif -%}
{{ render(subfield) }}
{{ render(subfield) }}
...
...
flask_admin/templates/admin/model/list.html
View file @
48ebb0fc
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
</li>
</li>
{% if admin_view.can_create %}
{% if admin_view.can_create %}
<li>
<li>
<a
href=
"{{ url_for('.create_view', url=return_url) }}"
>
{{ _gettext('Create') }}
</a>
<a
href=
"{{ url_for('.create_view', url=return_url) }}"
title=
"{{ _gettext('Create new record') }}"
>
{{ _gettext('Create') }}
</a>
</li>
</li>
{% endif %}
{% endif %}
...
@@ -54,7 +54,7 @@
...
@@ -54,7 +54,7 @@
{% block list_header scoped %}
{% block list_header scoped %}
{% if actions %}
{% if actions %}
<th
class=
"span1"
>
<th
class=
"span1"
>
<input
type=
"checkbox"
name=
"rowtoggle"
class=
"action-rowtoggle"
/>
<input
type=
"checkbox"
name=
"rowtoggle"
class=
"action-rowtoggle"
title=
"{{ _gettext('Select all records') }}"
/>
</th>
</th>
{% endif %}
{% endif %}
{% block list_row_actions_header %}
{% block list_row_actions_header %}
...
@@ -62,10 +62,10 @@
...
@@ -62,10 +62,10 @@
{% endblock %}
{% endblock %}
{% set column = 0 %}
{% set column = 0 %}
{% for c, name in list_columns %}
{% for c, name in list_columns %}
<th>
<th
class=
"column-header"
>
{% if admin_view.is_sortable(c) %}
{% if admin_view.is_sortable(c) %}
{% if sort_column == column %}
{% if sort_column == column %}
<a
href=
"{{ sort_url(column, True) }}"
>
<a
href=
"{{ sort_url(column, True) }}"
title=
"{{ _gettext('Sort by %(name)s', name=name) }}"
>
{{ name }}
{{ name }}
{% if sort_desc %}
{% if sort_desc %}
<i
class=
"icon-chevron-up"
></i>
<i
class=
"icon-chevron-up"
></i>
...
@@ -74,7 +74,7 @@
...
@@ -74,7 +74,7 @@
{% endif %}
{% endif %}
</a>
</a>
{% else %}
{% else %}
<a
href=
"{{ sort_url(column) }}"
>
{{ name }}
</a>
<a
href=
"{{ sort_url(column) }}"
title=
"{{ _gettext('Sort by %(name)s', name=name) }}"
>
{{ name }}
</a>
{% endif %}
{% endif %}
{% else %}
{% else %}
{{ name }}
{{ name }}
...
@@ -96,13 +96,13 @@
...
@@ -96,13 +96,13 @@
{% block list_row scoped %}
{% block list_row scoped %}
{% if actions %}
{% if actions %}
<td>
<td>
<input
type=
"checkbox"
name=
"rowid"
class=
"action-checkbox"
value=
"{{ get_pk_value(row) }}"
/>
<input
type=
"checkbox"
name=
"rowid"
class=
"action-checkbox"
value=
"{{ get_pk_value(row) }}"
title=
"{{ _gettext('Select record') }}"
/>
</td>
</td>
{% endif %}
{% endif %}
<td>
<td>
{% block list_row_actions scoped %}
{% block list_row_actions scoped %}
{%- if admin_view.can_edit -%}
{%- if admin_view.can_edit -%}
<a
class=
"icon"
href=
"{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}"
>
<a
class=
"icon"
href=
"{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}"
title=
"Edit record"
>
<i
class=
"icon-pencil"
></i>
<i
class=
"icon-pencil"
></i>
</a>
</a>
{%- endif -%}
{%- endif -%}
...
@@ -111,7 +111,7 @@
...
@@ -111,7 +111,7 @@
{% if csrf_token %}
{% if csrf_token %}
<input
type=
"hidden"
name=
"csrf_token"
value=
"{{ csrf_token() }}"
/>
<input
type=
"hidden"
name=
"csrf_token"
value=
"{{ csrf_token() }}"
/>
{% endif %}
{% endif %}
<button
onclick=
"return confirm('{{ _gettext('You sure you want to delete this item?') }}');"
>
<button
onclick=
"return confirm('{{ _gettext('You sure you want to delete this item?') }}');"
title=
"Delete record"
>
<i
class=
"icon-trash"
></i>
<i
class=
"icon-trash"
></i>
</button>
</button>
</form>
</form>
...
...
flask_admin/tests/sqlamodel/test_form_rules.py
View file @
48ebb0fc
...
@@ -139,3 +139,22 @@ def test_rule_inlinefieldlist():
...
@@ -139,3 +139,22 @@ def test_rule_inlinefieldlist():
rv
=
client
.
get
(
'/admin/model1view/new/'
)
rv
=
client
.
get
(
'/admin/model1view/new/'
)
eq_
(
rv
.
status_code
,
200
)
eq_
(
rv
.
status_code
,
200
)
def
test_inline_model_rules
():
app
,
db
,
admin
=
setup
()
Model1
,
Model2
=
create_models
(
db
)
db
.
create_all
()
view
=
CustomModelView
(
Model1
,
db
.
session
,
inline_models
=
[(
Model2
,
dict
(
form_rules
=
(
'string_field'
,
'bool_field'
)))])
admin
.
add_view
(
view
)
client
=
app
.
test_client
()
rv
=
client
.
get
(
'/admin/model1view/new/'
)
eq_
(
rv
.
status_code
,
200
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'int_field'
not
in
data
)
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