Commit efcc49ed authored by Serge S. Koval's avatar Serge S. Koval

Widget fixes, MongoEngine backend support

parent 8151015e
...@@ -39,4 +39,9 @@ class QueryAjaxModelLoader(AjaxModelLoader): ...@@ -39,4 +39,9 @@ class QueryAjaxModelLoader(AjaxModelLoader):
else: else:
criteria |= mongoengine.Q(**flt) criteria |= mongoengine.Q(**flt)
return query.filter(criteria).skip(offset).limit(limit) query = query.filter(criteria)
if offset:
query = query.skip(offset)
return query.limit(limit).all()
...@@ -122,6 +122,8 @@ class AjaxSelectField(SelectFieldBase): ...@@ -122,6 +122,8 @@ class AjaxSelectField(SelectFieldBase):
""" """
widget = AjaxSelect2Widget() widget = AjaxSelect2Widget()
separator = ','
def __init__(self, loader, label=None, validators=None, allow_blank=False, blank_text=u'', **kwargs): def __init__(self, loader, label=None, validators=None, allow_blank=False, blank_text=u'', **kwargs):
super(AjaxSelectField, self).__init__(label, validators, **kwargs) super(AjaxSelectField, self).__init__(label, validators, **kwargs)
self.loader = loader self.loader = loader
...@@ -170,6 +172,7 @@ class AjaxSelectMultipleField(AjaxSelectField): ...@@ -170,6 +172,7 @@ class AjaxSelectMultipleField(AjaxSelectField):
def __init__(self, loader, label=None, validators=None, default=None, **kwargs): def __init__(self, loader, label=None, validators=None, default=None, **kwargs):
if default is None: if default is None:
default = [] default = []
super(AjaxSelectMultipleField, self).__init__(loader, label, validators, default=default, **kwargs) super(AjaxSelectMultipleField, self).__init__(loader, label, validators, default=default, **kwargs)
self._invalid_formdata = False self._invalid_formdata = False
...@@ -198,12 +201,12 @@ class AjaxSelectMultipleField(AjaxSelectField): ...@@ -198,12 +201,12 @@ class AjaxSelectMultipleField(AjaxSelectField):
data = property(_get_data, _set_data) data = property(_get_data, _set_data)
def process_formdata(self, valuelist): def process_formdata(self, valuelist):
self._formdata = set(valuelist) self._formdata = set()
for field in valuelist:
for n in field.split(self.separator):
self._formdata.add(n)
def pre_validate(self, form): def pre_validate(self, form):
if self._invalid_formdata: if self._invalid_formdata:
raise ValidationError(self.gettext(u'Not a valid choice')) raise ValidationError(self.gettext(u'Not a valid choice'))
elif self.data:
for item in self.data:
if not self.loader.get_one(item):
raise ValidationError(self.gettext(u'Not a valid choice'))
from flask import url_for, json from flask import url_for, json
from wtforms.widgets import HTMLString, html_params from wtforms.widgets import HTMLString, html_params
from flask.ext.admin._compat import as_unicode
from flask.ext.admin.form import RenderTemplateWidget from flask.ext.admin.form import RenderTemplateWidget
...@@ -31,13 +32,23 @@ class AjaxSelect2Widget(object): ...@@ -31,13 +32,23 @@ class AjaxSelect2Widget(object):
if self.multiple: if self.multiple:
result = [] result = []
ids = []
for value in field.data: for value in field.data:
result.append(field.loader.format(value)) data = field.loader.format(value)
result.append(data)
ids.append(as_unicode(data[0]))
kwargs['value'] = json.dumps(result) separator = getattr(field, 'separator', ',')
kwargs['value'] = separator.join(ids)
kwargs['data-json'] = json.dumps(result)
kwargs['data-multiple'] = u'1' kwargs['data-multiple'] = u'1'
else: else:
kwargs['value'] = json.dumps(field.loader.format(field.data)) data = field.loader.format(field.data)
if data:
kwargs['value'] = data[0]
kwargs['data-json'] = json.dumps(data)
return HTMLString('<input %s>' % html_params(name=field.name, **kwargs)) return HTMLString('<input %s>' % html_params(name=field.name, **kwargs))
...@@ -37,7 +37,8 @@ ...@@ -37,7 +37,8 @@
} }
}, },
initSelection: function(element, callback) { initSelection: function(element, callback) {
var value = jQuery.parseJSON(element.val()); $el = $(element);
var value = jQuery.parseJSON($el.attr('data-json'));
var result = null; var result = null;
if (value) { if (value) {
......
...@@ -2,7 +2,7 @@ from nose.tools import eq_, ok_ ...@@ -2,7 +2,7 @@ from nose.tools import eq_, ok_
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
# Skip test on PY3 # Skip test on PY3
from flask.ext.admin._compat import PY2 from flask.ext.admin._compat import PY2, as_unicode
if not PY2: if not PY2:
raise SkipTest('MongoEngine is not Python 3 compatible') raise SkipTest('MongoEngine is not Python 3 compatible')
...@@ -350,3 +350,78 @@ def test_nested_list_subdocument(): ...@@ -350,3 +350,78 @@ def test_nested_list_subdocument():
ok_('name' in dir(inline_form)) ok_('name' in dir(inline_form))
ok_('value' not in dir(inline_form)) ok_('value' not in dir(inline_form))
def test_ajax_fk():
app, db, admin = setup()
class Model1(db.Document):
test1 = db.StringField(max_length=20)
test2 = db.StringField(max_length=20)
def __str__(self):
return self.test1
class Model2(db.Document):
int_field = db.IntField()
bool_field = db.BooleanField()
model1 = db.ReferenceField(Model1)
Model1.objects.delete()
Model2.objects.delete()
view = CustomModelView(
Model2,
url='view',
form_ajax_refs={
'model1': ('test1', 'test2')
}
)
admin.add_view(view)
ok_(u'model1' in view._form_ajax_refs)
model = Model1(test1=u'first')
model.save()
model2 = Model1(test1=u'foo', test2=u'bar').save()
# Check loader
loader = view._form_ajax_refs[u'model1']
mdl = loader.get_one(model.id)
eq_(mdl.test1, model.test1)
items = loader.get_list(u'fir')
eq_(len(items), 1)
eq_(items[0].id, model.id)
items = loader.get_list(u'bar')
eq_(len(items), 1)
eq_(items[0].test1, u'foo')
# Check form generation
form = view.create_form()
eq_(form.model1.__class__.__name__, u'AjaxSelectField')
with app.test_request_context('/admin/view/'):
ok_(u'value=""' not in form.model1())
form.model1.data = model
needle = u'data-json="[&quot;%s&quot;, &quot;first&quot;]"' % as_unicode(model.id)
ok_(needle in form.model1())
ok_(u'value="%s"' % as_unicode(model.id) in form.model1())
# Check querying
client = app.test_client()
req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo')
eq_(req.data, u'[["%s", "foo"]]' % model2.id)
# Check submitting
client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
mdl = Model2.objects.first()
ok_(mdl is not None)
ok_(mdl.model1 is not None)
eq_(mdl.model1.id, model.id)
eq_(mdl.model1.test1, u'first')
...@@ -698,7 +698,8 @@ def test_ajax_fk(): ...@@ -698,7 +698,8 @@ def test_ajax_fk():
ok_(u'model1' in view._form_ajax_refs) ok_(u'model1' in view._form_ajax_refs)
model = Model1(u'first') model = Model1(u'first')
db.session.add_all([model, Model1(u'foo', u'bar')]) model2 = Model1(u'foo', u'bar')
db.session.add_all([model, model2])
db.session.commit() db.session.commit()
# Check loader # Check loader
...@@ -719,16 +720,17 @@ def test_ajax_fk(): ...@@ -719,16 +720,17 @@ def test_ajax_fk():
eq_(form.model1.__class__.__name__, u'AjaxSelectField') eq_(form.model1.__class__.__name__, u'AjaxSelectField')
with app.test_request_context('/admin/view/'): with app.test_request_context('/admin/view/'):
ok_(u'value="null"' in form.model1()) ok_(u'value=""' not in form.model1())
form.model1.data = model form.model1.data = model
ok_(u'value="[1, &quot;first&quot;]"' in form.model1()) ok_(u'data-json="[%s, &quot;first&quot;]"' % model.id in form.model1())
ok_(u'value="1"' in form.model1())
# Check querying # Check querying
client = app.test_client() client = app.test_client()
req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo') req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo')
eq_(req.data, u'[[2, "foo"]]') eq_(req.data, u'[[%s, "foo"]]' % model2.id)
# Check submitting # Check submitting
req = client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) req = client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
...@@ -738,3 +740,67 @@ def test_ajax_fk(): ...@@ -738,3 +740,67 @@ def test_ajax_fk():
ok_(mdl.model1 is not None) ok_(mdl.model1 is not None)
eq_(mdl.model1.id, model.id) eq_(mdl.model1.id, model.id)
eq_(mdl.model1.test1, u'first') eq_(mdl.model1.test1, u'first')
def test_ajax_fk_multi():
app, db, admin = setup()
class Model1(db.Model):
__tablename__ = 'model1'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
def __str__(self):
return self.name
table = db.Table('m2m', db.Model.metadata,
db.Column('model1_id', db.Integer, db.ForeignKey('model1.id')),
db.Column('model2_id', db.Integer, db.ForeignKey('model2.id'))
)
class Model2(db.Model):
__tablename__ = 'model2'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
model1_id = db.Column(db.Integer(), db.ForeignKey(Model1.id))
model1 = db.relationship(Model1, backref='models2', secondary=table)
db.create_all()
view = CustomModelView(
Model2, db.session,
url='view',
form_ajax_refs={
'model1': ('name',)
}
)
admin.add_view(view)
ok_(u'model1' in view._form_ajax_refs)
model = Model1(name=u'first')
db.session.add_all([model, Model1(name=u'foo')])
db.session.commit()
# Check form generation
form = view.create_form()
eq_(form.model1.__class__.__name__, u'AjaxSelectMultipleField')
with app.test_request_context('/admin/view/'):
print form.model1()
ok_(u'data-json="[]"' in form.model1())
form.model1.data = [model]
ok_(u'data-json="[[1, &quot;first&quot;]]"' in form.model1())
# Check submitting
client = app.test_client()
client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
mdl = db.session.query(Model2).first()
ok_(mdl is not None)
ok_(mdl.model1 is not None)
eq_(len(mdl.model1), 1)
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