Commit e9aba295 authored by Paul Brown's avatar Paul Brown

make suggested changes to editable list view implementation

parent eb37f32c
...@@ -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
......
...@@ -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)
......
...@@ -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.
......
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
...@@ -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):
""" """
......
...@@ -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.
......
...@@ -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)
......
...@@ -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 %}
......
...@@ -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')
......
...@@ -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()
......
...@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment