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
2a724048
Commit
2a724048
authored
Jan 02, 2016
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1160 from pawl/fix_editable_list2
simplify editable list view, fix list view multiple select
parents
4ff21f12
6f4ef689
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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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 @
2a724048
...
...
@@ -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