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
6f4ef689
Commit
6f4ef689
authored
Dec 31, 2015
by
Paul Brown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
simplify editable list view, fix list view multiple select
parent
d7df4fe4
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
170 additions
and
192 deletions
+170
-192
view.py
flask_admin/contrib/mongoengine/view.py
+6
-9
view.py
flask_admin/contrib/peewee/view.py
+6
-9
fields.py
flask_admin/contrib/sqla/fields.py
+3
-3
form.py
flask_admin/contrib/sqla/form.py
+0
-6
view.py
flask_admin/contrib/sqla/view.py
+6
-10
base.py
flask_admin/model/base.py
+17
-22
fields.py
flask_admin/model/fields.py
+1
-53
form.py
flask_admin/model/form.py
+18
-10
widgets.py
flask_admin/model/widgets.py
+33
-25
form.js
flask_admin/static/admin/js/form.js
+27
-2
lib.html
flask_admin/templates/bootstrap2/admin/lib.html
+1
-1
list.html
flask_admin/templates/bootstrap2/admin/model/list.html
+2
-2
lib.html
flask_admin/templates/bootstrap3/admin/lib.html
+1
-1
list.html
flask_admin/templates/bootstrap3/admin/model/list.html
+2
-2
test_basic.py
flask_admin/tests/mongoengine/test_basic.py
+13
-11
test_basic.py
flask_admin/tests/peeweemodel/test_basic.py
+17
-12
test_basic.py
flask_admin/tests/sqla/test_basic.py
+17
-14
No files found.
flask_admin/contrib/mongoengine/view.py
View file @
6f4ef689
...
...
@@ -5,8 +5,7 @@ from flask import request, flash, abort, Response
from
flask_admin
import
expose
from
flask_admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask_admin.model
import
BaseModelView
from
flask_admin.model.form
import
wrap_fields_in_fieldlist
from
flask_admin.model.fields
import
ListEditableFieldList
from
flask_admin.model.form
import
create_editable_list_form
from
flask_admin._compat
import
iteritems
,
string_types
import
mongoengine
...
...
@@ -426,17 +425,16 @@ class ModelView(BaseModelView):
return
form_class
def
scaffold_list_form
(
self
,
custom_fieldlist
=
ListEditableFieldList
,
validators
=
None
):
def
scaffold_list_form
(
self
,
widget
=
None
,
validators
=
None
):
"""
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
:param widget:
WTForms widget class. Defaults to `XEditableWidget`.
:param validators:
`form_args` dict with only validators
{'name': {'validators': [required()]}}
:param custom_fieldlist:
A WTForm FieldList class. By default, `ListEditableFieldList`.
"""
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
...
...
@@ -444,9 +442,8 @@ class ModelView(BaseModelView):
only
=
self
.
column_editable_list
,
field_args
=
validators
)
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
,
custom_fieldlist
)
return
create_editable_list_form
(
self
.
form_base_class
,
form_class
,
widget
)
# AJAX foreignkey support
def
_create_ajax_loader
(
self
,
name
,
opts
):
...
...
flask_admin/contrib/peewee/view.py
View file @
6f4ef689
...
...
@@ -5,8 +5,7 @@ from flask import flash
from
flask_admin._compat
import
string_types
,
iteritems
from
flask_admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask_admin.model
import
BaseModelView
from
flask_admin.model.form
import
wrap_fields_in_fieldlist
from
flask_admin.model.fields
import
ListEditableFieldList
from
flask_admin.model.form
import
create_editable_list_form
from
peewee
import
PrimaryKeyField
,
ForeignKeyField
,
Field
,
CharField
,
TextField
...
...
@@ -265,26 +264,24 @@ class ModelView(BaseModelView):
return
form_class
def
scaffold_list_form
(
self
,
custom_fieldlist
=
ListEditableFieldList
,
validators
=
None
):
def
scaffold_list_form
(
self
,
widget
=
None
,
validators
=
None
):
"""
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
:param widget:
WTForms widget class. Defaults to `XEditableWidget`.
:param validators:
`form_args` dict with only validators
{'name': {'validators': [required()]}}
:param custom_fieldlist:
A WTForm FieldList class. By default, `ListEditableFieldList`.
"""
form_class
=
get_form
(
self
.
model
,
self
.
model_form_converter
(
self
),
base_class
=
self
.
form_base_class
,
only
=
self
.
column_editable_list
,
field_args
=
validators
)
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
,
custom_fieldlist
)
return
create_editable_list_form
(
self
.
form_base_class
,
form_class
,
widget
)
def
scaffold_inline_form_models
(
self
,
form_class
):
converter
=
self
.
model_form_converter
(
self
)
...
...
flask_admin/contrib/sqla/fields.py
View file @
6f4ef689
...
...
@@ -14,7 +14,7 @@ except ImportError:
from
.tools
import
get_primary_key
from
flask_admin._compat
import
text_type
,
string_types
,
iteritems
from
flask_admin.form
import
FormOpts
,
BaseForm
from
flask_admin.form
import
FormOpts
,
BaseForm
,
Select2Widget
from
flask_admin.model.fields
import
InlineFieldList
,
InlineModelFormField
from
flask_admin.babel
import
lazy_gettext
...
...
@@ -55,7 +55,7 @@ class QuerySelectField(SelectFieldBase):
being `None`. The label for this blank choice can be set by specifying the
`blank_text` parameter.
"""
widget
=
widgets
.
Selec
t
()
widget
=
Select2Widge
t
()
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
query_factory
=
None
,
get_pk
=
None
,
get_label
=
None
,
allow_blank
=
False
,
...
...
@@ -136,7 +136,7 @@ class QuerySelectMultipleField(QuerySelectField):
If any of the items in the data list or submitted form data cannot be
found in the query, this will result in a validation error.
"""
widget
=
widgets
.
Selec
t
(
multiple
=
True
)
widget
=
Select2Widge
t
(
multiple
=
True
)
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
default
=
None
,
**
kwargs
):
if
default
is
None
:
...
...
flask_admin/contrib/sqla/form.py
View file @
6f4ef689
...
...
@@ -80,12 +80,6 @@ class AdminModelConverter(ModelConverterBase):
if
'query_factory'
not
in
kwargs
:
kwargs
[
'query_factory'
]
=
lambda
:
self
.
session
.
query
(
remote_model
)
if
'widget'
not
in
kwargs
:
if
multiple
:
kwargs
[
'widget'
]
=
form
.
Select2Widget
(
multiple
=
True
)
else
:
kwargs
[
'widget'
]
=
form
.
Select2Widget
()
if
multiple
:
return
QuerySelectMultipleField
(
**
kwargs
)
else
:
...
...
flask_admin/contrib/sqla/view.py
View file @
6f4ef689
...
...
@@ -15,9 +15,7 @@ from flask import flash
from
flask_admin._compat
import
string_types
,
text_type
from
flask_admin.babel
import
gettext
,
ngettext
,
lazy_gettext
from
flask_admin.model
import
BaseModelView
from
flask_admin.model.form
import
wrap_fields_in_fieldlist
from
flask_admin.model.fields
import
ListEditableFieldList
from
flask_admin.model.form
import
create_editable_list_form
from
flask_admin.actions
import
action
from
flask_admin._backwards
import
ObsoleteAttr
...
...
@@ -675,17 +673,16 @@ class ModelView(BaseModelView):
return
form_class
def
scaffold_list_form
(
self
,
custom_fieldlist
=
ListEditableFieldList
,
validators
=
None
):
def
scaffold_list_form
(
self
,
widget
=
None
,
validators
=
None
):
"""
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
:param widget:
WTForms widget class. Defaults to `XEditableWidget`.
:param validators:
`form_args` dict with only validators
{'name': {'validators': [required()]}}
:param custom_fieldlist:
A WTForm FieldList class. By default, `ListEditableFieldList`.
"""
converter
=
self
.
model_form_converter
(
self
.
session
,
self
)
form_class
=
form
.
get_form
(
self
.
model
,
converter
,
...
...
@@ -693,9 +690,8 @@ class ModelView(BaseModelView):
only
=
self
.
column_editable_list
,
field_args
=
validators
)
return
wrap_fields_in_fieldlist
(
self
.
form_base_class
,
form_class
,
custom_fieldlist
)
return
create_editable_list_form
(
self
.
form_base_class
,
form_class
,
widget
)
def
scaffold_inline_form_models
(
self
,
form_class
):
"""
...
...
flask_admin/model/base.py
View file @
6f4ef689
...
...
@@ -26,7 +26,6 @@ from flask_admin._compat import (iteritems, itervalues, OrderedDict,
as_unicode
,
csv_encode
,
text_type
)
from
.helpers
import
prettify_name
,
get_mdict_item_or_list
from
.ajax
import
AjaxModelLoader
from
.fields
import
ListEditableFieldList
# Used to generate filter query string name
filter_char_re
=
re
.
compile
(
'[^a-z0-9 ]'
)
...
...
@@ -1069,17 +1068,16 @@ class BaseModelView(BaseView, ActionsMixin):
"""
raise
NotImplementedError
(
'Please implement scaffold_form method'
)
def
scaffold_list_form
(
self
,
custom_fieldlist
=
ListEditableFieldList
,
validators
=
None
):
def
scaffold_list_form
(
self
,
widget
=
None
,
validators
=
None
):
"""
Create form for the `index_view` using only the columns from
`self.column_editable_list`.
:param widget:
WTForms widget class. Defaults to `XEditableWidget`.
:param validators:
`form_args` dict with only validators
{'name': {'validators': [DataRequired()]}}
:param custom_fieldlist:
A WTForm FieldList class. By default, `ListEditableFieldList`.
Must be implemented in the child class.
"""
...
...
@@ -1107,7 +1105,6 @@ class BaseModelView(BaseView, ActionsMixin):
Allows overriding the editable list view field/widget. For example::
from flask_admin.model.fields import ListEditableFieldList
from flask_admin.model.widgets import XEditableWidget
class CustomWidget(XEditableWidget):
...
...
@@ -1119,12 +1116,9 @@ class BaseModelView(BaseView, ActionsMixin):
return kwargs
class CustomFieldList(ListEditableFieldList):
widget = CustomWidget()
class MyModelView(BaseModelView):
def get_list_form(self):
return self.scaffold_list_form(
CustomFieldLis
t)
return self.scaffold_list_form(
widget=CustomWidge
t)
"""
if
self
.
form_args
:
# get only validators, other form_args can break FieldList wrapper
...
...
@@ -1716,7 +1710,7 @@ class BaseModelView(BaseView, ActionsMixin):
List view
"""
if
self
.
column_editable_list
:
form
=
self
.
list_form
()
form
=
self
.
list_form
else
:
form
=
None
...
...
@@ -2074,24 +2068,23 @@ class BaseModelView(BaseView, ActionsMixin):
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
# delete all fields except the
submitted fields and csrf token
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'
:
if
(
field
.
name
in
request
.
form
)
or
(
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
):
pk
=
form
.
list_form_pk
.
data
record
=
self
.
get_one
(
pk
)
if
record
is
None
:
return
gettext
(
'Record does not exist.'
),
500
if
self
.
update_model
(
form
,
record
):
# Success
return
gettext
(
'Record was successfully saved.'
)
...
...
@@ -2105,6 +2098,8 @@ class BaseModelView(BaseView, ActionsMixin):
for
error
in
field
.
errors
:
# return validation error to x-editable
if
isinstance
(
error
,
list
):
return
", "
.
join
(
error
),
500
return
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
", "
.
join
(
error
)),
500
else
:
return
error
,
500
return
gettext
(
'Failed to update record.
%(error)
s'
,
error
=
error
),
500
flask_admin/model/fields.py
View file @
6f4ef689
...
...
@@ -10,7 +10,7 @@ except ImportError:
from
flask_admin._compat
import
iteritems
from
.widgets
import
(
InlineFieldListWidget
,
InlineFormWidget
,
AjaxSelect2Widget
,
XEditableWidget
)
AjaxSelect2Widget
)
class
InlineFieldList
(
FieldList
):
...
...
@@ -126,58 +126,6 @@ class InlineModelFormField(FormField):
field
.
populate_obj
(
obj
,
name
)
class
ListEditableFieldList
(
FieldList
):
"""
Modified FieldList to allow for alphanumeric primary keys.
Used in the editable list view.
"""
widget
=
XEditableWidget
()
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
ListEditableFieldList
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
# min_entries = 1 is required for the widget to determine the type
self
.
min_entries
=
1
def
_extract_indices
(
self
,
prefix
,
formdata
):
offset
=
len
(
prefix
)
+
1
for
name
in
formdata
:
# selects only relevant field (not CSRF, other fields, etc)
if
name
.
startswith
(
prefix
):
# exclude offset (prefix-), remaining text is the index
yield
name
[
offset
:]
def
_add_entry
(
self
,
formdata
=
None
,
data
=
unset_value
,
index
=
None
):
assert
not
self
.
max_entries
or
len
(
self
.
entries
)
<
self
.
max_entries
,
\
'You cannot have more than max_entries entries in this FieldList'
if
index
is
None
:
index
=
self
.
last_index
+
1
self
.
last_index
=
index
# '%s-%s' instead of '%s-%d' to allow alphanumeric
name
=
'
%
s-
%
s'
%
(
self
.
short_name
,
index
)
id
=
'
%
s-
%
s'
%
(
self
.
id
,
index
)
# support both wtforms 1 and 2
meta
=
getattr
(
self
,
'meta'
,
None
)
if
meta
:
field
=
self
.
unbound_field
.
bind
(
form
=
None
,
name
=
name
,
prefix
=
self
.
_prefix
,
id
=
id
,
_meta
=
meta
)
else
:
field
=
self
.
unbound_field
.
bind
(
form
=
None
,
name
=
name
,
prefix
=
self
.
_prefix
,
id
=
id
)
field
.
process
(
formdata
,
data
)
self
.
entries
.
append
(
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
):
"""
Ajax Model Select Field
...
...
flask_admin/model/form.py
View file @
6f4ef689
...
...
@@ -3,7 +3,10 @@ import inspect
from
flask_admin.form
import
BaseForm
,
rules
from
flask_admin._compat
import
iteritems
from
wtforms.fields
import
HiddenField
from
wtforms.fields.core
import
UnboundField
from
wtforms.validators
import
InputRequired
from
.widgets
import
XEditableWidget
def
converts
(
*
args
):
...
...
@@ -13,7 +16,7 @@ def converts(*args):
return
_inner
def
wrap_fields_in_fieldlist
(
form_base_class
,
form_class
,
CustomFieldList
):
def
create_editable_list_form
(
form_base_class
,
form_class
,
widget
=
None
):
"""
Create a form class with all the fields wrapped in a FieldList.
...
...
@@ -26,20 +29,25 @@ def wrap_fields_in_fieldlist(form_base_class, form_class, CustomFieldList):
WTForms form class, by default `form_base_class` from base.
:param form_class:
WTForms form class generated by `form.get_form`.
:param CustomFieldList:
WTForms FieldList class.
By default, `CustomFieldList` is `ListEditableFieldList`.
:param widget:
WTForms widget class. Defaults to `XEditableWidget`.
"""
class
FieldListForm
(
form_base_class
):
pass
if
widget
is
None
:
widget
=
XEditableWidget
class
ListForm
(
form_base_class
):
list_form_pk
=
HiddenField
(
validators
=
[
InputRequired
()])
# iterate FormMeta to get unbound fields
# iterate FormMeta to get unbound fields
, replace widget, copy to ListForm
for
name
,
obj
in
iteritems
(
form_class
.
__dict__
):
if
isinstance
(
obj
,
UnboundField
):
# wrap field in a WTForms FieldList
setattr
(
FieldListForm
,
name
,
CustomFieldList
(
obj
))
obj
.
kwargs
[
'widget'
]
=
XEditableWidget
()
setattr
(
ListForm
,
name
,
obj
)
if
name
==
"list_form_pk"
:
raise
Exception
(
'Form already has a list_form_pk column.'
)
return
Field
ListForm
return
ListForm
class
InlineBaseFormAdmin
(
object
):
...
...
flask_admin/model/widgets.py
View file @
6f4ef689
...
...
@@ -72,7 +72,7 @@ class XEditableWidget(object):
field inside of the FieldList (StringField, IntegerField, etc).
"""
def
__call__
(
self
,
field
,
**
kwargs
):
kwargs
.
setdefault
(
'data-value'
,
kwargs
.
pop
(
'value'
,
''
))
kwargs
.
setdefault
(
'data-value'
,
kwargs
.
pop
(
'
display_
value'
,
''
))
kwargs
.
setdefault
(
'data-role'
,
'x-editable'
)
kwargs
.
setdefault
(
'data-url'
,
'./ajax/update/'
)
...
...
@@ -87,26 +87,23 @@ class XEditableWidget(object):
kwargs
[
'data-csrf'
]
=
kwargs
.
pop
(
"csrf"
,
""
)
# subfield is the first entry (subfield) from FieldList (field)
subfield
=
field
.
entries
[
0
]
kwargs
=
self
.
get_kwargs
(
subfield
,
kwargs
)
kwargs
=
self
.
get_kwargs
(
field
,
kwargs
)
return
HTMLString
(
'<a
%
s>
%
s</a>'
%
(
html_params
(
**
kwargs
),
escape
(
kwargs
[
'data-value'
]))
)
def
get_kwargs
(
self
,
sub
field
,
kwargs
):
def
get_kwargs
(
self
,
field
,
kwargs
):
"""
Return extra kwargs based on the
sub
field type.
Return extra kwargs based on the field type.
"""
if
sub
field
.
type
==
'StringField'
:
if
field
.
type
==
'StringField'
:
kwargs
[
'data-type'
]
=
'text'
elif
sub
field
.
type
==
'TextAreaField'
:
elif
field
.
type
==
'TextAreaField'
:
kwargs
[
'data-type'
]
=
'textarea'
kwargs
[
'data-rows'
]
=
'5'
elif
sub
field
.
type
==
'BooleanField'
:
elif
field
.
type
==
'BooleanField'
:
kwargs
[
'data-type'
]
=
'select'
# data-source = dropdown options
kwargs
[
'data-source'
]
=
json
.
dumps
([
...
...
@@ -114,53 +111,64 @@ class XEditableWidget(object):
{
'value'
:
'1'
,
'text'
:
gettext
(
'Yes'
)}
])
kwargs
[
'data-role'
]
=
'x-editable-boolean'
elif
sub
field
.
type
==
'Select2Field'
:
elif
field
.
type
==
'Select2Field'
:
kwargs
[
'data-type'
]
=
'select'
choices
=
[{
'value'
:
x
,
'text'
:
y
}
for
x
,
y
in
sub
field
.
choices
]
choices
=
[{
'value'
:
x
,
'text'
:
y
}
for
x
,
y
in
field
.
choices
]
# prepend a blank field to choices if allow_blank = True
if
getattr
(
sub
field
,
'allow_blank'
,
False
):
if
getattr
(
field
,
'allow_blank'
,
False
):
choices
.
insert
(
0
,
{
'value'
:
'__None'
,
'text'
:
''
})
# json.dumps fixes issue with unicode strings not loading correctly
kwargs
[
'data-source'
]
=
json
.
dumps
(
choices
)
elif
sub
field
.
type
==
'DateField'
:
elif
field
.
type
==
'DateField'
:
kwargs
[
'data-type'
]
=
'combodate'
kwargs
[
'data-format'
]
=
'YYYY-MM-DD'
kwargs
[
'data-template'
]
=
'YYYY-MM-DD'
elif
sub
field
.
type
==
'DateTimeField'
:
elif
field
.
type
==
'DateTimeField'
:
kwargs
[
'data-type'
]
=
'combodate'
kwargs
[
'data-format'
]
=
'YYYY-MM-DD HH:mm:ss'
kwargs
[
'data-template'
]
=
'YYYY-MM-DD HH:mm:ss'
# x-editable-combodate uses 1 minute increments
kwargs
[
'data-role'
]
=
'x-editable-combodate'
elif
sub
field
.
type
==
'TimeField'
:
elif
field
.
type
==
'TimeField'
:
kwargs
[
'data-type'
]
=
'combodate'
kwargs
[
'data-format'
]
=
'HH:mm:ss'
kwargs
[
'data-template'
]
=
'HH:mm:ss'
kwargs
[
'data-role'
]
=
'x-editable-combodate'
elif
sub
field
.
type
==
'IntegerField'
:
elif
field
.
type
==
'IntegerField'
:
kwargs
[
'data-type'
]
=
'number'
elif
sub
field
.
type
in
[
'FloatField'
,
'DecimalField'
]:
elif
field
.
type
in
[
'FloatField'
,
'DecimalField'
]:
kwargs
[
'data-type'
]
=
'number'
kwargs
[
'data-step'
]
=
'any'
elif
subfield
.
type
in
[
'QuerySelectField'
,
'ModelSelectField'
]:
elif
field
.
type
in
[
'QuerySelectField'
,
'ModelSelectField'
,
'QuerySelectMultipleField'
]:
# QuerySelectField and ModelSelectField are for relations
kwargs
[
'data-type'
]
=
'select'
choices
=
[]
for
choice
in
subfield
:
selected_ids
=
[]
for
value
,
label
,
selected
in
field
.
iter_choices
():
try
:
choices
.
append
({
'value'
:
text_type
(
choice
.
_value
()),
'text'
:
text_type
(
choice
.
label
.
text
)})
label
=
text_type
(
label
)
except
TypeError
:
# unable to display text value
choices
.
append
({
'value'
:
text_type
(
choice
.
_value
()),
'text'
:
''
})
label
=
''
choices
.
append
({
'value'
:
text_type
(
value
),
'text'
:
label
})
if
selected
:
selected_ids
.
append
(
value
)
# blank field is already included if allow_blank
kwargs
[
'data-source'
]
=
json
.
dumps
(
choices
)
if
field
.
type
==
'QuerySelectMultipleField'
:
kwargs
[
'data-type'
]
=
'select2'
kwargs
[
'data-role'
]
=
'x-editable-select2-multiple'
# must use id instead of text or prefilled values won't work
separator
=
getattr
(
field
,
'separator'
,
','
)
kwargs
[
'data-value'
]
=
separator
.
join
(
selected_ids
)
else
:
raise
Exception
(
'Unsupported field type:
%
s'
%
(
type
(
sub
field
),))
raise
Exception
(
'Unsupported field type:
%
s'
%
(
type
(
field
),))
return
kwargs
flask_admin/static/admin/js/form.js
View file @
6f4ef689
...
...
@@ -272,11 +272,12 @@
return
true
;
}
// make x-editable's POST
act like a normal FieldList field
// make x-editable's POST
compatible with WTForms
// for x-editable, x-editable-combodate, and x-editable-boolean cases
var
overrideXeditableParams
=
function
(
params
)
{
var
newParams
=
{};
newParams
[
params
.
name
+
'-'
+
params
.
pk
]
=
params
.
value
;
newParams
[
'list_form_pk'
]
=
params
.
pk
;
newParams
[
params
.
name
]
=
params
.
value
;
if
(
$
(
this
).
data
(
'csrf'
))
{
newParams
[
'csrf_token'
]
=
$
(
this
).
data
(
'csrf'
);
}
...
...
@@ -451,6 +452,30 @@
}
});
return
true
;
case
'x-editable-select2-multiple'
:
$el
.
editable
({
params
:
overrideXeditableParams
,
ajaxOptions
:
{
// prevents keys with the same value from getting converted into arrays
traditional
:
true
},
select2
:
{
multiple
:
true
},
display
:
function
(
value
)
{
// override to display text instead of ids on list view
var
html
=
[];
var
data
=
$
.
fn
.
editableutils
.
itemsByValue
(
value
,
$el
.
data
(
'source'
),
'id'
);
if
(
data
.
length
)
{
$
.
each
(
data
,
function
(
i
,
v
)
{
html
.
push
(
$
.
fn
.
editableutils
.
escape
(
v
.
text
));
});
$
(
this
).
html
(
html
.
join
(
', '
));
}
else
{
$
(
this
).
empty
();
}
}
});
return
true
;
case
'x-editable-boolean'
:
$el
.
editable
({
params
:
overrideXeditableParams
,
...
...
flask_admin/templates/bootstrap2/admin/lib.html
View file @
6f4ef689
...
...
@@ -249,5 +249,5 @@
{% if editable_columns %}
<script
src=
"{{ admin_static.url(filename='vendor/x-editable/js/bootstrap2-editable.min.js', v='1.5.1') }}"
></script>
{% endif %}
<script
src=
"{{ admin_static.url(filename='admin/js/form.js', v='1.0.
0
') }}"
></script>
<script
src=
"{{ admin_static.url(filename='admin/js/form.js', v='1.0.
1
') }}"
></script>
{% endmacro %}
flask_admin/templates/bootstrap2/admin/model/list.html
View file @
6f4ef689
...
...
@@ -158,9 +158,9 @@
<td
class=
"col-{{c}}"
>
{% if admin_view.is_editable(c) %}
{% if form.csrf_token %}
{{ form
[c](pk=get_pk_value(row),
value=get_value(row, c), csrf=form.csrf_token._value()) }}
{{ form
(obj=row)[c](pk=get_pk_value(row), display_
value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% else %}
{{ form
[c](pk=get_pk_value(row),
value=get_value(row, c)) }}
{{ form
(obj=row)[c](pk=get_pk_value(row), display_
value=get_value(row, c)) }}
{% endif %}
{% else %}
{{ get_value(row, c) }}
...
...
flask_admin/templates/bootstrap3/admin/lib.html
View file @
6f4ef689
...
...
@@ -233,5 +233,5 @@
{% if editable_columns %}
<script
src=
"{{ admin_static.url(filename='vendor/x-editable/js/bootstrap3-editable.min.js', v='1.5.1') }}"
></script>
{% endif %}
<script
src=
"{{ admin_static.url(filename='admin/js/form.js', v='1.0.
0
') }}"
></script>
<script
src=
"{{ admin_static.url(filename='admin/js/form.js', v='1.0.
1
') }}"
></script>
{% endmacro %}
flask_admin/templates/bootstrap3/admin/model/list.html
View file @
6f4ef689
...
...
@@ -157,9 +157,9 @@
<td
class=
"col-{{c}}"
>
{% if admin_view.is_editable(c) %}
{% if form.csrf_token %}
{{ form
[c](pk=get_pk_value(row),
value=get_value(row, c), csrf=form.csrf_token._value()) }}
{{ form
(obj=row)[c](pk=get_pk_value(row), display_
value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% else %}
{{ form
[c](pk=get_pk_value(row),
value=get_value(row, c)) }}
{{ form
(obj=row)[c](pk=get_pk_value(row), display_
value=get_value(row, c)) }}
{% endif %}
{% else %}
{{ get_value(row, c) }}
...
...
flask_admin/tests/mongoengine/test_basic.py
View file @
6f4ef689
...
...
@@ -148,8 +148,7 @@ def test_column_editable_list():
Model1
,
Model2
=
create_models
(
db
)
view
=
CustomModelView
(
Model1
,
column_editable_list
=
[
'test1'
,
'datetime_field'
])
column_editable_list
=
[
'test1'
,
'datetime_field'
])
admin
.
add_view
(
view
)
fill_db
(
Model1
,
Model2
)
...
...
@@ -164,7 +163,8 @@ def test_column_editable_list():
# Form - Test basic in-line edit functionality
obj1
=
Model1
.
objects
.
get
(
test1
=
'test1_val_3'
)
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-'
+
str
(
obj1
.
id
):
'change-success-1'
,
'list_form_pk'
:
str
(
obj1
.
id
),
'test1'
:
'change-success-1'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
@@ -177,33 +177,35 @@ def test_column_editable_list():
# Test validation error
obj2
=
Model1
.
objects
.
get
(
test1
=
'datetime_obj1'
)
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'datetime_field-'
+
str
(
obj2
.
id
):
'problematic-input'
,
'list_form_pk'
:
str
(
obj2
.
id
),
'datetime_field'
:
'problematic-input'
,
})
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
'list_form_pk'
:
'1000'
,
'test1'
:
'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'
,
'list_form_pk'
:
'1'
,
'test2'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
ok_
(
'problematic-input'
not
in
data
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
'model1'
])
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
'model1'
])
admin
.
add_view
(
view
)
obj3
=
Model2
.
objects
.
get
(
string_field
=
'string_field_val_1'
)
rv
=
client
.
post
(
'/admin/model2/ajax/update/'
,
data
=
{
'model1-'
+
str
(
obj3
.
id
):
str
(
obj1
.
id
),
'list_form_pk'
:
str
(
obj3
.
id
),
'model1'
:
str
(
obj1
.
id
),
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
flask_admin/tests/peeweemodel/test_basic.py
View file @
6f4ef689
...
...
@@ -185,9 +185,10 @@ def test_column_editable_list():
Model1
,
Model2
=
create_models
(
db
)
view
=
CustomModelView
(
Model1
,
column_editable_list
=
[
'test1'
,
'enum_field'
])
# wtf-peewee doesn't automatically add length validators for max_length
form_args
=
{
'test1'
:
{
'validators'
:
[
validators
.
Length
(
max
=
20
)]}}
view
=
CustomModelView
(
Model1
,
column_editable_list
=
[
'test1'
],
form_args
=
form_args
)
admin
.
add_view
(
view
)
fill_db
(
Model1
,
Model2
)
...
...
@@ -201,7 +202,8 @@ def test_column_editable_list():
# Form - Test basic in-line edit functionality
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1'
:
'change-success-1'
,
'list_form_pk'
:
'1'
,
'test1'
:
'change-success-1'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
@@ -213,32 +215,35 @@ def test_column_editable_list():
# Test validation error
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'enum_field-1'
:
'problematic-input'
,
'list_form_pk'
:
'1'
,
'test1'
:
'longerthantwentycharacterslongerthantwentycharacterslongerthantwentycharacterslongerthantwentycharacters'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
'list_form_pk'
:
'1000'
,
'test1'
:
'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'
,
'list_form_pk'
:
'1'
,
'test2'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
ok_
(
'problematic-input'
not
in
data
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
'model1'
])
view
=
CustomModelView
(
Model2
,
column_editable_list
=
[
'model1'
])
admin
.
add_view
(
view
)
rv
=
client
.
post
(
'/admin/model2/ajax/update/'
,
data
=
{
'model1-1'
:
'3'
,
'list_form_pk'
:
'1'
,
'model1'
:
'3'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
flask_admin/tests/sqla/test_basic.py
View file @
6f4ef689
...
...
@@ -354,8 +354,7 @@ def test_column_editable_list():
Model1
,
Model2
=
create_models
(
db
)
view
=
CustomModelView
(
Model1
,
db
.
session
,
column_editable_list
=
[
'test1'
,
'enum_field'
])
column_editable_list
=
[
'test1'
,
'enum_field'
])
admin
.
add_view
(
view
)
fill_db
(
db
,
Model1
,
Model2
)
...
...
@@ -369,7 +368,8 @@ def test_column_editable_list():
# Form - Test basic in-line edit functionality
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1'
:
'change-success-1'
,
'list_form_pk'
:
'1'
,
'test1'
:
'change-success-1'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
@@ -381,32 +381,34 @@ def test_column_editable_list():
# Test validation error
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'enum_field-1'
:
'problematic-input'
,
'list_form_pk'
:
'1'
,
'enum_field'
:
'problematic-input'
,
})
eq_
(
rv
.
status_code
,
500
)
# Test invalid primary key
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'test1-1000'
:
'problematic-input'
,
'list_form_pk'
:
'1000'
,
'test1'
:
'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'
,
'list_form_pk'
:
'1'
,
'test2'
:
'problematic-input'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
eq_
(
rv
.
status_code
,
500
)
ok_
(
'problematic-input'
not
in
data
)
# Test in-line editing for relations
view
=
CustomModelView
(
Model2
,
db
.
session
,
column_editable_list
=
[
'model1'
])
view
=
CustomModelView
(
Model2
,
db
.
session
,
column_editable_list
=
[
'model1'
])
admin
.
add_view
(
view
)
rv
=
client
.
post
(
'/admin/model2/ajax/update/'
,
data
=
{
'model1-1'
:
'3'
,
'list_form_pk'
:
'1'
,
'model1'
:
'3'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
@@ -495,7 +497,8 @@ def test_editable_list_special_pks():
# Form - Test basic in-line edit functionality
rv
=
client
.
post
(
'/admin/model1/ajax/update/'
,
data
=
{
'val1-1-1'
:
'change-success-1'
,
'list_form_pk'
:
'1-1'
,
'val1'
:
'change-success-1'
,
})
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'Record was successfully saved.'
==
data
)
...
...
@@ -1511,8 +1514,8 @@ def test_form_onetoone():
eq_
(
model1
.
model2
,
model2
)
eq_
(
model2
.
model1
,
model1
)
eq_
(
view1
.
_create_form_class
.
model2
.
kwargs
[
'widget'
]
.
multiple
,
False
)
eq_
(
view2
.
_create_form_class
.
model1
.
kwargs
[
'widget'
]
.
multiple
,
False
)
eq_
(
view1
.
_create_form_class
.
model2
.
field_class
.
widget
.
multiple
,
False
)
eq_
(
view2
.
_create_form_class
.
model1
.
field_class
.
widget
.
multiple
,
False
)
def
test_relations
():
...
...
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