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
e9aba295
Commit
e9aba295
authored
Jan 06, 2015
by
Paul Brown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
make suggested changes to editable list view implementation
parent
eb37f32c
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
185 additions
and
219 deletions
+185
-219
view.py
flask_admin/contrib/mongoengine/view.py
+8
-46
view.py
flask_admin/contrib/peewee/view.py
+6
-48
view.py
flask_admin/contrib/sqla/view.py
+7
-49
base.py
flask_admin/model/base.py
+55
-48
fields.py
flask_admin/model/fields.py
+6
-2
form.py
flask_admin/model/form.py
+24
-0
widgets.py
flask_admin/model/widgets.py
+1
-1
lib.html
flask_admin/templates/bootstrap3/admin/lib.html
+1
-1
test_basic.py
flask_admin/tests/mongoengine/test_basic.py
+21
-7
test_basic.py
flask_admin/tests/peeweemodel/test_basic.py
+32
-12
test_basic.py
flask_admin/tests/sqla/test_basic.py
+24
-5
No files found.
flask_admin/contrib/mongoengine/view.py
View file @
e9aba295
...
@@ -5,17 +5,15 @@ from flask import request, flash, abort, Response
...
@@ -5,17 +5,15 @@ from flask import request, flash, abort, Response
from
flask.ext.admin
import
expose
from
flask.ext.admin
import
expose
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model.form
import
wrap_fields_in_fieldlist
from
flask.ext.admin._compat
import
iteritems
,
string_types
from
flask.ext.admin._compat
import
iteritems
,
string_types
from
flask.ext.admin.actions
import
action
from
flask.ext.admin.model.fields
import
ListEditableFieldList
from
wtforms.fields.core
import
UnboundField
import
mongoengine
import
mongoengine
import
gridfs
import
gridfs
from
mongoengine.connection
import
get_db
from
mongoengine.connection
import
get_db
from
bson.objectid
import
ObjectId
from
bson.objectid
import
ObjectId
from
flask.ext.admin.actions
import
action
from
.filters
import
FilterConverter
,
BaseMongoEngineFilter
from
.filters
import
FilterConverter
,
BaseMongoEngineFilter
from
.form
import
get_form
,
CustomModelConverter
from
.form
import
get_form
,
CustomModelConverter
from
.typefmt
import
DEFAULT_FORMATTERS
from
.typefmt
import
DEFAULT_FORMATTERS
...
@@ -403,20 +401,15 @@ class ModelView(BaseModelView):
...
@@ -403,20 +401,15 @@ class ModelView(BaseModelView):
def
scaffold_list_form
(
self
):
def
scaffold_list_form
(
self
):
"""
"""
Create form for the list view editable columns.
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
"""
"""
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
base_class
=
self
.
form_base_class
,
base_class
=
self
.
form_base_class
,
only
=
self
.
column_editable_list
)
only
=
self
.
column_editable_list
)
# iterate FormMeta to get unbound fields
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
)
field_dict
=
{}
for
name
,
field_object
in
iteritems
(
form_class
.
__dict__
):
if
not
name
.
startswith
(
'_'
)
and
isinstance
(
field_object
,
UnboundField
):
# wrap each field in the form from get_form in FieldList
field_dict
[
name
]
=
ListEditableFieldList
(
field_object
)
return
type
(
self
.
model
.
__name__
+
'Form'
,
(
self
.
form_base_class
,
),
field_dict
)
# AJAX foreignkey support
# AJAX foreignkey support
def
_create_ajax_loader
(
self
,
name
,
opts
):
def
_create_ajax_loader
(
self
,
name
,
opts
):
...
@@ -565,37 +558,6 @@ class ModelView(BaseModelView):
...
@@ -565,37 +558,6 @@ class ModelView(BaseModelView):
return
True
return
True
def
update_list_model
(
self
,
form
):
"""
Update model from the list view.
Only supports updating a single field at a time.
:param form:
Form instance
"""
try
:
model
=
self
.
model
()
for
field
in
form
:
# FieldList's last_index will only be set if a field is submitted
# last_index will be the primary key of the updated record
if
getattr
(
field
,
'last_index'
,
None
):
record
=
self
.
get_one
(
field
.
last_index
)
setattr
(
record
,
field
.
name
,
field
.
data
.
pop
())
self
.
_on_model_change
(
form
,
model
,
False
)
record
.
save
()
self
.
after_model_change
(
form
,
model
,
False
)
return
True
except
Exception
as
ex
:
if
not
self
.
handle_view_exception
(
ex
):
log
.
exception
(
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
self
.
session
.
rollback
()
# Error: Unable to update database or no records were changed.
return
False
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
"""
"""
Delete model helper
Delete model helper
...
...
flask_admin/contrib/peewee/view.py
View file @
e9aba295
...
@@ -2,18 +2,16 @@ import logging
...
@@ -2,18 +2,16 @@ import logging
from
flask
import
flash
from
flask
import
flash
from
flask.ext.admin._compat
import
string_types
,
iteritems
from
flask.ext.admin._compat
import
string_types
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model.form
import
wrap_fields_in_fieldlist
from
peewee
import
PrimaryKeyField
,
ForeignKeyField
,
Field
,
CharField
,
TextField
from
peewee
import
PrimaryKeyField
,
ForeignKeyField
,
Field
,
CharField
,
TextField
from
flask.ext.admin.actions
import
action
from
flask.ext.admin.actions
import
action
from
flask.ext.admin.contrib.peewee
import
filters
from
flask.ext.admin.contrib.peewee
import
filters
from
flask.ext.admin.model.fields
import
ListEditableFieldList
from
wtforms.fields.core
import
UnboundField
from
.form
import
get_form
,
CustomModelConverter
,
InlineModelConverter
,
save_inline
from
.form
import
get_form
,
CustomModelConverter
,
InlineModelConverter
,
save_inline
from
.tools
import
get_primary_key
,
parse_like_term
from
.tools
import
get_primary_key
,
parse_like_term
from
.ajax
import
create_ajax_loader
from
.ajax
import
create_ajax_loader
...
@@ -242,20 +240,14 @@ class ModelView(BaseModelView):
...
@@ -242,20 +240,14 @@ class ModelView(BaseModelView):
def
scaffold_list_form
(
self
):
def
scaffold_list_form
(
self
):
"""
"""
Create form for the list view editable columns.
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
"""
"""
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
base_class
=
self
.
form_base_class
,
base_class
=
self
.
form_base_class
,
only
=
self
.
column_editable_list
)
only
=
self
.
column_editable_list
)
# iterate FormMeta to get unbound fields
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
)
field_dict
=
{}
for
name
,
field_object
in
iteritems
(
form_class
.
__dict__
):
if
not
name
.
startswith
(
'_'
)
and
isinstance
(
field_object
,
UnboundField
):
# wrap each field in the form from get_form in FieldList
field_dict
[
name
]
=
ListEditableFieldList
(
field_object
)
return
type
(
self
.
model
.
__name__
+
'Form'
,
(
self
.
form_base_class
,
),
field_dict
)
def
scaffold_inline_form_models
(
self
,
form_class
):
def
scaffold_inline_form_models
(
self
,
form_class
):
converter
=
self
.
model_form_converter
(
self
)
converter
=
self
.
model_form_converter
(
self
)
...
@@ -402,40 +394,6 @@ class ModelView(BaseModelView):
...
@@ -402,40 +394,6 @@ class ModelView(BaseModelView):
return
True
return
True
def
update_list_model
(
self
,
form
):
"""
Update model from the list view.
Only supports updating a single field at a time.
:param form:
Form instance
"""
try
:
model
=
self
.
model
()
for
field
in
form
:
# FieldList's last_index will only be set if a field is submitted
# last_index will be the primary key of the updated record
if
getattr
(
field
,
'last_index'
,
None
):
record
=
self
.
get_one
(
field
.
last_index
)
setattr
(
record
,
field
.
name
,
field
.
data
.
pop
())
self
.
_on_model_change
(
form
,
model
,
False
)
record
.
save
()
# For peewee have to save inline forms after model was saved
save_inline
(
form
,
model
)
self
.
after_model_change
(
form
,
model
,
False
)
return
True
except
Exception
as
ex
:
if
not
self
.
handle_view_exception
(
ex
):
log
.
exception
(
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
self
.
session
.
rollback
()
# Error: Unable to update database or no records were changed.
return
False
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
try
:
try
:
self
.
on_model_delete
(
model
)
self
.
on_model_delete
(
model
)
...
...
flask_admin/contrib/sqla/view.py
View file @
e9aba295
...
@@ -8,21 +8,19 @@ from sqlalchemy.exc import IntegrityError
...
@@ -8,21 +8,19 @@ from sqlalchemy.exc import IntegrityError
from
flask
import
flash
from
flask
import
flash
from
flask.ext.admin._compat
import
string_types
,
iteritems
from
flask.ext.admin._compat
import
string_types
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model.form
import
wrap_fields_in_fieldlist
from
flask.ext.admin.actions
import
action
from
flask.ext.admin.actions
import
action
from
flask.ext.admin._backwards
import
ObsoleteAttr
from
flask.ext.admin._backwards
import
ObsoleteAttr
from
flask.ext.admin.model.fields
import
ListEditableFieldList
from
wtforms.fields.core
import
UnboundField
from
flask.ext.admin.contrib.sqla
import
form
,
filters
,
tools
from
flask.ext.admin.contrib.sqla
import
form
,
filters
,
tools
from
.typefmt
import
DEFAULT_FORMATTERS
from
.typefmt
import
DEFAULT_FORMATTERS
from
.tools
import
get_query_for_ids
from
.tools
import
get_query_for_ids
from
.ajax
import
create_ajax_loader
from
.ajax
import
create_ajax_loader
# Set up logger
# Set up logger
log
=
logging
.
getLogger
(
"flask-admin.sqla"
)
log
=
logging
.
getLogger
(
"flask-admin.sqla"
)
...
@@ -616,24 +614,15 @@ class ModelView(BaseModelView):
...
@@ -616,24 +614,15 @@ class ModelView(BaseModelView):
def
scaffold_list_form
(
self
):
def
scaffold_list_form
(
self
):
"""
"""
Create form for the list view editable columns.
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
The form is created using the existing get_form(),
but each field is wrapped in a WTForms FieldList.
"""
"""
converter
=
self
.
model_form_converter
(
self
.
session
,
self
)
converter
=
self
.
model_form_converter
(
self
.
session
,
self
)
form_class
=
form
.
get_form
(
self
.
model
,
converter
,
form_class
=
form
.
get_form
(
self
.
model
,
converter
,
base_class
=
self
.
form_base_class
,
base_class
=
self
.
form_base_class
,
only
=
self
.
column_editable_list
)
only
=
self
.
column_editable_list
)
# iterate FormMeta to get unbound fields
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
)
field_dict
=
{}
for
name
,
field_object
in
iteritems
(
form_class
.
__dict__
):
if
not
name
.
startswith
(
'_'
)
and
isinstance
(
field_object
,
UnboundField
):
# wrap each field in the form from get_form in FieldList
field_dict
[
name
]
=
ListEditableFieldList
(
field_object
)
return
type
(
self
.
model
.
__name__
+
'Form'
,
(
self
.
form_base_class
,
),
field_dict
)
def
scaffold_inline_form_models
(
self
,
form_class
):
def
scaffold_inline_form_models
(
self
,
form_class
):
"""
"""
...
@@ -922,37 +911,6 @@ class ModelView(BaseModelView):
...
@@ -922,37 +911,6 @@ class ModelView(BaseModelView):
return
True
return
True
def
update_list_model
(
self
,
form
):
"""
Update model from the list view.
Only supports updating a single field at a time.
:param form:
Form instance
"""
try
:
model
=
self
.
model
()
for
field
in
form
:
# FieldList's last_index will only be set if a field is submitted
# last_index will be the primary key of the updated record
if
getattr
(
field
,
'last_index'
,
None
):
record
=
self
.
session
.
query
(
self
.
model
)
.
get
(
field
.
last_index
)
setattr
(
record
,
field
.
name
,
field
.
data
.
pop
())
self
.
_on_model_change
(
form
,
model
,
False
)
self
.
session
.
commit
()
self
.
after_model_change
(
form
,
model
,
False
)
return
True
except
Exception
as
ex
:
if
not
self
.
handle_view_exception
(
ex
):
log
.
exception
(
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
self
.
session
.
rollback
()
# Error: Unable to update database or no records were changed.
return
False
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
"""
"""
Delete model.
Delete model.
...
...
flask_admin/model/base.py
View file @
e9aba295
import
warnings
import
warnings
import
re
import
re
from
flask
import
request
,
redirect
,
flash
,
abort
,
json
,
Response
from
flask
import
(
request
,
redirect
,
flash
,
abort
,
json
,
Response
,
get_flashed_messages
)
from
jinja2
import
contextfunction
from
jinja2
import
contextfunction
from
wtforms.validators
import
ValidationError
from
wtforms.validators
import
ValidationError
...
@@ -11,8 +12,8 @@ from flask.ext.admin.base import BaseView, expose
...
@@ -11,8 +12,8 @@ from flask.ext.admin.base import BaseView, expose
from
flask.ext.admin.form
import
BaseForm
,
FormOpts
,
rules
from
flask.ext.admin.form
import
BaseForm
,
FormOpts
,
rules
from
flask.ext.admin.model
import
filters
,
typefmt
from
flask.ext.admin.model
import
filters
,
typefmt
from
flask.ext.admin.actions
import
ActionsMixin
from
flask.ext.admin.actions
import
ActionsMixin
from
flask.ext.admin.helpers
import
(
get_form_data
,
validate_form_on_submit
,
from
flask.ext.admin.helpers
import
(
get_form_data
,
validate_form_on_submit
,
get_redirect_target
,
is_form_submitted
)
get_redirect_target
)
from
flask.ext.admin.tools
import
rec_getattr
from
flask.ext.admin.tools
import
rec_getattr
from
flask.ext.admin._backwards
import
ObsoleteAttr
from
flask.ext.admin._backwards
import
ObsoleteAttr
from
flask.ext.admin._compat
import
iteritems
,
OrderedDict
,
as_unicode
from
flask.ext.admin._compat
import
iteritems
,
OrderedDict
,
as_unicode
...
@@ -274,7 +275,7 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -274,7 +275,7 @@ class BaseModelView(BaseView, ActionsMixin):
class MyModelView(BaseModelView):
class MyModelView(BaseModelView):
column_editable_list = ('name', 'last_name')
column_editable_list = ('name', 'last_name')
"""
"""
column_choices
=
None
column_choices
=
None
"""
"""
Map choices to columns in list view
Map choices to columns in list view
...
@@ -590,9 +591,9 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -590,9 +591,9 @@ class BaseModelView(BaseView, ActionsMixin):
self
.
_create_form_class
=
self
.
get_create_form
()
self
.
_create_form_class
=
self
.
get_create_form
()
self
.
_edit_form_class
=
self
.
get_edit_form
()
self
.
_edit_form_class
=
self
.
get_edit_form
()
# List View In-Line Editing
# List View In-Line Editing
if
self
.
column_editable_list
:
if
self
.
column_editable_list
:
self
.
_list_form_class
=
self
.
scaffold_list_form
()
self
.
_list_form_class
=
self
.
scaffold_list_form
()
else
:
else
:
self
.
column_editable_list
=
{}
self
.
column_editable_list
=
{}
...
@@ -852,7 +853,8 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -852,7 +853,8 @@ class BaseModelView(BaseView, ActionsMixin):
def
scaffold_list_form
(
self
):
def
scaffold_list_form
(
self
):
"""
"""
Create form class for list view in-line editing.
Create form for the `index_view` using only the columns from
`self.column_editable_list`. Must be implemented in the child class.
"""
"""
raise
NotImplementedError
(
'Please implement scaffold_list_form method'
)
raise
NotImplementedError
(
'Please implement scaffold_list_form method'
)
...
@@ -937,7 +939,7 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -937,7 +939,7 @@ class BaseModelView(BaseView, ActionsMixin):
Column name.
Column name.
"""
"""
return
name
in
self
.
column_editable_list
return
name
in
self
.
column_editable_list
def
_get_column_by_idx
(
self
,
idx
):
def
_get_column_by_idx
(
self
,
idx
):
"""
"""
Return column index by
Return column index by
...
@@ -1114,15 +1116,6 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -1114,15 +1116,6 @@ class BaseModelView(BaseView, ActionsMixin):
"""
"""
raise
NotImplementedError
()
raise
NotImplementedError
()
def
update_list_model
(
self
,
form
,
model
):
"""
Update model from the list view.
:param form:
Form instance
"""
raise
NotImplementedError
()
def
delete_model
(
self
,
model
):
def
delete_model
(
self
,
model
):
"""
"""
Delete model.
Delete model.
...
@@ -1291,42 +1284,13 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -1291,42 +1284,13 @@ class BaseModelView(BaseView, ActionsMixin):
raise
NotImplementedError
()
raise
NotImplementedError
()
# Views
# Views
@
expose
(
'/'
,
methods
=
(
'POST'
,
'GET'
)
)
@
expose
(
'/'
)
def
index_view
(
self
):
def
index_view
(
self
):
"""
"""
List view
List view
"""
"""
if
self
.
column_editable_list
:
if
self
.
column_editable_list
:
form
=
self
.
list_form
()
form
=
self
.
list_form
()
# prevent validation issues due to submitting a single field
# delete all fields except the field being submitted
if
is_form_submitted
():
for
field
in
form
:
# only submitted fields have last_index
if
getattr
(
field
,
'last_index'
,
None
):
pass
elif
field
.
name
==
'csrf_token'
:
pass
else
:
form
.
__delitem__
(
field
.
name
)
if
self
.
validate_form
(
form
):
if
self
.
update_list_model
(
form
):
return
gettext
(
'Record was successfully saved.'
)
else
:
# No records changed, or error saving to database.
return
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
''
),
500
if
form
.
errors
:
for
field
in
form
:
for
error
in
field
.
errors
:
# return error to x-editable
if
isinstance
(
error
,
list
):
return
", "
.
join
(
error
),
500
else
:
return
error
,
500
else
:
else
:
form
=
None
form
=
None
...
@@ -1376,7 +1340,7 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -1376,7 +1340,7 @@ class BaseModelView(BaseView, ActionsMixin):
self
.
list_template
,
self
.
list_template
,
data
=
data
,
data
=
data
,
form
=
form
,
form
=
form
,
# List
# List
list_columns
=
self
.
_list_columns
,
list_columns
=
self
.
_list_columns
,
sortable_columns
=
self
.
_sortable_columns
,
sortable_columns
=
self
.
_sortable_columns
,
...
@@ -1526,3 +1490,46 @@ class BaseModelView(BaseView, ActionsMixin):
...
@@ -1526,3 +1490,46 @@ class BaseModelView(BaseView, ActionsMixin):
data
=
[
loader
.
format
(
m
)
for
m
in
loader
.
get_list
(
query
,
offset
,
limit
)]
data
=
[
loader
.
format
(
m
)
for
m
in
loader
.
get_list
(
query
,
offset
,
limit
)]
return
Response
(
json
.
dumps
(
data
),
mimetype
=
'application/json'
)
return
Response
(
json
.
dumps
(
data
),
mimetype
=
'application/json'
)
@
expose
(
'/ajax/update/'
,
methods
=
(
'POST'
,))
def
ajax_update
(
self
):
"""
Edits a single column of a record in list view.
"""
if
not
self
.
column_editable_list
:
abort
(
404
)
record
=
None
form
=
self
.
list_form
()
# prevent validation issues due to submitting a single field
# delete all fields except the field being submitted
for
field
in
form
:
# only the submitted field has a positive last_index
if
getattr
(
field
,
'last_index'
,
0
):
record
=
self
.
get_one
(
str
(
field
.
last_index
))
elif
field
.
name
==
'csrf_token'
:
pass
else
:
form
.
__delitem__
(
field
.
name
)
if
record
is
None
:
return
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
''
),
500
if
self
.
validate_form
(
form
):
if
self
.
update_model
(
form
,
record
):
# Success
return
gettext
(
'Record was successfully saved.'
)
else
:
# Error: No records changed, or problem saving to database.
msgs
=
", "
.
join
([
msg
for
msg
in
get_flashed_messages
()])
return
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
msgs
),
500
else
:
for
field
in
form
:
for
error
in
field
.
errors
:
# return validation error to x-editable
if
isinstance
(
error
,
list
):
return
", "
.
join
(
error
),
500
else
:
return
error
,
500
flask_admin/model/fields.py
View file @
e9aba295
...
@@ -9,7 +9,7 @@ except ImportError:
...
@@ -9,7 +9,7 @@ except ImportError:
from
wtforms.utils
import
unset_value
from
wtforms.utils
import
unset_value
from
flask.ext.admin._compat
import
iteritems
from
flask.ext.admin._compat
import
iteritems
from
.widgets
import
(
InlineFieldListWidget
,
InlineFormWidget
,
from
.widgets
import
(
InlineFieldListWidget
,
InlineFormWidget
,
AjaxSelect2Widget
,
XEditableWidget
)
AjaxSelect2Widget
,
XEditableWidget
)
...
@@ -129,7 +129,7 @@ class InlineModelFormField(FormField):
...
@@ -129,7 +129,7 @@ class InlineModelFormField(FormField):
class
ListEditableFieldList
(
FieldList
):
class
ListEditableFieldList
(
FieldList
):
"""
"""
Modified FieldList to allow for alphanumeric primary keys.
Modified FieldList to allow for alphanumeric primary keys.
Used in the editable list view.
Used in the editable list view.
"""
"""
widget
=
XEditableWidget
()
widget
=
XEditableWidget
()
...
@@ -173,6 +173,10 @@ class ListEditableFieldList(FieldList):
...
@@ -173,6 +173,10 @@ class ListEditableFieldList(FieldList):
self
.
entries
.
append
(
field
)
self
.
entries
.
append
(
field
)
return
field
return
field
def
populate_obj
(
self
,
obj
,
name
):
# return data from first item, instead of a list of items
setattr
(
obj
,
name
,
self
.
data
.
pop
())
class
AjaxSelectField
(
SelectFieldBase
):
class
AjaxSelectField
(
SelectFieldBase
):
"""
"""
...
...
flask_admin/model/form.py
View file @
e9aba295
...
@@ -3,6 +3,9 @@ import inspect
...
@@ -3,6 +3,9 @@ import inspect
from
flask.ext.admin.form
import
BaseForm
,
rules
from
flask.ext.admin.form
import
BaseForm
,
rules
from
flask.ext.admin._compat
import
iteritems
from
flask.ext.admin._compat
import
iteritems
from
.fields
import
ListEditableFieldList
from
wtforms.fields.core
import
UnboundField
def
converts
(
*
args
):
def
converts
(
*
args
):
def
_inner
(
func
):
def
_inner
(
func
):
...
@@ -11,6 +14,27 @@ def converts(*args):
...
@@ -11,6 +14,27 @@ def converts(*args):
return
_inner
return
_inner
def
wrap_fields_in_fieldlist
(
form_base_class
,
form_class
):
"""
Create a form class with all the fields wrapped in a FieldList.
Wrapping each field in FieldList allows submitting POST requests
in this format: ('<field_name>-<primary_key>', '<value>')
Used in the editable list view.
"""
class
FieldListForm
(
form_base_class
):
pass
# iterate FormMeta to get unbound fields
for
name
,
obj
in
iteritems
(
form_class
.
__dict__
):
if
isinstance
(
obj
,
UnboundField
):
# wrap field in a WTForms FieldList
setattr
(
FieldListForm
,
name
,
ListEditableFieldList
(
obj
))
return
FieldListForm
class
InlineBaseFormAdmin
(
object
):
class
InlineBaseFormAdmin
(
object
):
"""
"""
Settings for inline form administration.
Settings for inline form administration.
...
...
flask_admin/model/widgets.py
View file @
e9aba295
...
@@ -74,7 +74,7 @@ class XEditableWidget(object):
...
@@ -74,7 +74,7 @@ class XEditableWidget(object):
value
=
kwargs
.
pop
(
"value"
,
""
)
value
=
kwargs
.
pop
(
"value"
,
""
)
kwargs
.
setdefault
(
'data-role'
,
'x-editable'
)
kwargs
.
setdefault
(
'data-role'
,
'x-editable'
)
kwargs
.
setdefault
(
'data-url'
,
'./'
)
kwargs
.
setdefault
(
'data-url'
,
'./
ajax/update/
'
)
kwargs
.
setdefault
(
'id'
,
field
.
id
)
kwargs
.
setdefault
(
'id'
,
field
.
id
)
kwargs
.
setdefault
(
'name'
,
field
.
name
)
kwargs
.
setdefault
(
'name'
,
field
.
name
)
...
...
flask_admin/templates/bootstrap3/admin/lib.html
View file @
e9aba295
...
@@ -147,7 +147,7 @@
...
@@ -147,7 +147,7 @@
<hr>
<hr>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<div
class=
"col-md-offset-2 col-md-10 submit-row"
>
<div
class=
"col-md-offset-2 col-md-10 submit-row"
>
<input
type=
"submit"
class=
"btn btn-primary"
value=
"{{ _gettext('Submit') }}"
/>
<input
type=
"submit"
class=
"btn btn-primary"
value=
"{{ _gettext('Submit') }}"
/>
{% if extra %}
{% if extra %}
{{ extra }}
{{ extra }}
{% endif %}
{% endif %}
...
...
flask_admin/tests/mongoengine/test_basic.py
View file @
e9aba295
...
@@ -162,7 +162,7 @@ def test_column_editable_list():
...
@@ -162,7 +162,7 @@ def test_column_editable_list():
# Form - Test basic in-line edit functionality
# Form - Test basic in-line edit functionality
obj1
=
Model1
.
objects
.
get
(
test1
=
'test1_val_3'
)
obj1
=
Model1
.
objects
.
get
(
test1
=
'test1_val_3'
)
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'test1-'
+
str
(
obj1
.
id
):
'change-success-1'
,
'test1-'
+
str
(
obj1
.
id
):
'change-success-1'
,
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
...
@@ -173,21 +173,35 @@ def test_column_editable_list():
...
@@ -173,21 +173,35 @@ def test_column_editable_list():
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'change-success-1'
in
data
)
ok_
(
'change-success-1'
in
data
)
# Test
errors
# Test
validation error
obj2
=
Model1
.
objects
.
get
(
test1
=
'datetime_obj1'
)
obj2
=
Model1
.
objects
.
get
(
test1
=
'datetime_obj1'
)
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'datetime_field-'
+
str
(
obj2
.
id
):
'problematic-input'
,
'datetime_field-'
+
str
(
obj2
.
id
):
'problematic-input'
,
})
})
eq_
(
rv
.
status_code
,
500
)
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test editing column not in column_editable_list
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test2-1'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
column_editable_list
=
[
'model1'
])
'model1'
])
admin
.
add_view
(
view
)
admin
.
add_view
(
view
)
# Test in-line editing for relations
obj3
=
Model2
.
objects
.
get
(
string_field
=
'string_field_val_1'
)
obj3
=
Model2
.
objects
.
get
(
string_field
=
'string_field_val_1'
)
rv
=
client
.
post
(
'/admin/model2/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model2/
ajax/update/
'
,
data
=
{
'model1-'
+
str
(
obj3
.
id
):
str
(
obj1
.
id
),
'model1-'
+
str
(
obj3
.
id
):
str
(
obj1
.
id
),
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
...
...
flask_admin/tests/peeweemodel/test_basic.py
View file @
e9aba295
...
@@ -57,29 +57,30 @@ def create_models(db):
...
@@ -57,29 +57,30 @@ def create_models(db):
date_field
=
peewee
.
DateField
(
null
=
True
)
date_field
=
peewee
.
DateField
(
null
=
True
)
timeonly_field
=
peewee
.
TimeField
(
null
=
True
)
timeonly_field
=
peewee
.
TimeField
(
null
=
True
)
datetime_field
=
peewee
.
DateTimeField
(
null
=
True
)
datetime_field
=
peewee
.
DateTimeField
(
null
=
True
)
def
__str__
(
self
):
def
__str__
(
self
):
return
self
.
test1
# "or ''" fixes error when loading choices for relation field:
# TypeError: coercing to Unicode: need string or buffer, NoneType found
return
self
.
test1
or
''
class
Model2
(
BaseModel
):
class
Model2
(
BaseModel
):
def
__init__
(
self
,
char_field
=
None
,
int_field
=
None
,
float_field
=
None
,
def
__init__
(
self
,
char_field
=
None
,
int_field
=
None
,
float_field
=
None
,
bool_field
=
0
,
model1
=
None
):
bool_field
=
0
):
super
(
Model2
,
self
)
.
__init__
()
super
(
Model2
,
self
)
.
__init__
()
self
.
char_field
=
char_field
self
.
char_field
=
char_field
self
.
int_field
=
int_field
self
.
int_field
=
int_field
self
.
float_field
=
float_field
self
.
float_field
=
float_field
self
.
bool_field
=
bool_field
self
.
bool_field
=
bool_field
self
.
model1
=
model1
char_field
=
peewee
.
CharField
(
max_length
=
20
)
char_field
=
peewee
.
CharField
(
max_length
=
20
)
int_field
=
peewee
.
IntegerField
(
null
=
True
)
int_field
=
peewee
.
IntegerField
(
null
=
True
)
float_field
=
peewee
.
FloatField
(
null
=
True
)
float_field
=
peewee
.
FloatField
(
null
=
True
)
bool_field
=
peewee
.
BooleanField
()
bool_field
=
peewee
.
BooleanField
()
# Relation
# Relation
model1
=
peewee
.
ForeignKeyField
(
Model1
,
null
=
True
)
model1
=
peewee
.
ForeignKeyField
(
Model1
,
null
=
True
)
Model1
.
create_table
()
Model1
.
create_table
()
Model2
.
create_table
()
Model2
.
create_table
()
...
@@ -198,7 +199,7 @@ def test_column_editable_list():
...
@@ -198,7 +199,7 @@ def test_column_editable_list():
ok_
(
'data-role="x-editable"'
in
data
)
ok_
(
'data-role="x-editable"'
in
data
)
# Form - Test basic in-line edit functionality
# Form - Test basic in-line edit functionality
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'test1-1'
:
'change-success-1'
,
'test1-1'
:
'change-success-1'
,
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
...
@@ -209,24 +210,43 @@ def test_column_editable_list():
...
@@ -209,24 +210,43 @@ def test_column_editable_list():
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'change-success-1'
in
data
)
ok_
(
'change-success-1'
in
data
)
# Test
errors
# Test
validation error
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'enum_field-1'
:
'problematic-input'
,
'enum_field-1'
:
'problematic-input'
,
})
})
eq_
(
rv
.
status_code
,
500
)
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test editing column not in column_editable_list
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test2-1'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
column_editable_list
=
[
'model1'
])
'model1'
])
admin
.
add_view
(
view
)
admin
.
add_view
(
view
)
# Test in-line editing for relations
rv
=
client
.
post
(
'/admin/model2/ajax/update/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model2/'
,
data
=
{
'model1-1'
:
'3'
,
'model1-1'
:
'3'
,
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
ok_
(
'Record was successfully saved.'
==
data
)
# confirm the value has changed
rv
=
client
.
get
(
'/admin/model2/'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'test1_val_3'
in
data
)
def
test_column_filters
():
def
test_column_filters
():
app
,
db
,
admin
=
setup
()
app
,
db
,
admin
=
setup
()
...
...
flask_admin/tests/sqla/test_basic.py
View file @
e9aba295
...
@@ -339,7 +339,7 @@ def test_column_editable_list():
...
@@ -339,7 +339,7 @@ def test_column_editable_list():
ok_
(
'data-role="x-editable"'
in
data
)
ok_
(
'data-role="x-editable"'
in
data
)
# Form - Test basic in-line edit functionality
# Form - Test basic in-line edit functionality
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'test1-1'
:
'change-success-1'
,
'test1-1'
:
'change-success-1'
,
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
...
@@ -350,24 +350,43 @@ def test_column_editable_list():
...
@@ -350,24 +350,43 @@ def test_column_editable_list():
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'change-success-1'
in
data
)
ok_
(
'change-success-1'
in
data
)
# Test
errors
# Test
validation error
rv
=
client
.
post
(
'/admin/model1/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model1/
ajax/update/
'
,
data
=
{
'enum_field-1'
:
'problematic-input'
,
'enum_field-1'
:
'problematic-input'
,
})
})
eq_
(
rv
.
status_code
,
500
)
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test editing column not in column_editable_list
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test2-1'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
db
.
session
,
view
=
CustomModelView
(
Model2
,
db
.
session
,
column_editable_list
=
[
column_editable_list
=
[
'model1'
])
'model1'
])
admin
.
add_view
(
view
)
admin
.
add_view
(
view
)
# Test in-line editing for relations
rv
=
client
.
post
(
'/admin/model2/ajax/update/'
,
data
=
{
rv
=
client
.
post
(
'/admin/model2/'
,
data
=
{
'model1-1'
:
'3'
,
'model1-1'
:
'3'
,
})
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
ok_
(
'Record was successfully saved.'
==
data
)
# confirm the value has changed
rv
=
client
.
get
(
'/admin/model2/'
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'test1_val_3'
in
data
)
def
test_column_filters
():
def
test_column_filters
():
app
,
db
,
admin
=
setup
()
app
,
db
,
admin
=
setup
()
...
...
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