Commit 9344f9a0 authored by Serge S. Koval's avatar Serge S. Koval

Peewee backend support

parent efcc49ed
......@@ -32,7 +32,7 @@ class QueryAjaxModelLoader(AjaxModelLoader):
criteria = None
for field in self.fields:
flt = {'%s__icontains' % field.name: term}
flt = {u'%s__icontains' % field.name: term}
if not criteria:
criteria = mongoengine.Q(**flt)
......
import mongoengine
from flask.ext.admin._compat import as_unicode
from flask.ext.admin.model.ajax import AjaxModelLoader, DEFAULT_PAGE_SIZE
from .tools import get_primary_key
class QueryAjaxModelLoader(AjaxModelLoader):
def __init__(self, name, model, fields):
"""
Constructor.
:param fields:
Fields to run query against
"""
super(QueryAjaxModelLoader, self).__init__(name)
self.model = model
self.fields = fields
self.pk = get_primary_key(model)
def format(self, model):
if not model:
return None
return (getattr(model, self.pk), as_unicode(model))
def get_one(self, pk):
return self.model.get(**{self.pk: pk})
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
query = self.model.select()
stmt = None
for field in self.fields:
q = field ** (u'%%%s%%' % term)
if stmt is None:
stmt = q
else:
stmt |= q
query = query.where(stmt)
if offset:
query = query.offset(offset)
return list(query.limit(limit).execute())
......@@ -8,7 +8,7 @@ from wtfpeewee.orm import ModelConverter, model_form
from flask.ext.admin import form
from flask.ext.admin._compat import iteritems, itervalues
from flask.ext.admin.model.form import InlineFormAdmin, InlineModelConverterBase
from flask.ext.admin.model.fields import InlineModelFormField, InlineFieldList
from flask.ext.admin.model.fields import InlineModelFormField, InlineFieldList, AjaxSelectField
from .tools import get_primary_key
......@@ -80,13 +80,26 @@ class InlineModelFormList(InlineFieldList):
class CustomModelConverter(ModelConverter):
def __init__(self, additional=None):
def __init__(self, view, additional=None):
super(CustomModelConverter, self).__init__(additional)
self.view = view
self.converters[PrimaryKeyField] = self.handle_pk
self.converters[DateTimeField] = self.handle_datetime
self.converters[DateField] = self.handle_date
self.converters[TimeField] = self.handle_time
def handle_foreign_key(self, model, field, **kwargs):
loader = self.view._form_ajax_refs.get(field.name)
if loader:
if field.null:
kwargs['allow_blank'] = True
return field.name, AjaxSelectField(loader, **kwargs)
return super(CustomModelConverter, self).handle_foreign_key(model, field, **kwargs)
def handle_pk(self, model, field, **kwargs):
kwargs['validators'] = []
return field.name, fields.HiddenField(**kwargs)
......
......@@ -2,7 +2,6 @@ import logging
from flask import flash
from flask.ext.admin import form
from flask.ext.admin._compat import string_types
from flask.ext.admin.babel import gettext, ngettext, lazy_gettext
from flask.ext.admin.model import BaseModelView
......@@ -11,8 +10,10 @@ from peewee import PrimaryKeyField, ForeignKeyField, Field, CharField, TextField
from flask.ext.admin.actions import action
from flask.ext.admin.contrib.peewee import filters
from .form import get_form, CustomModelConverter, InlineModelConverter, save_inline
from .tools import get_primary_key, parse_like_term
from .ajax import QueryAjaxModelLoader
class ModelView(BaseModelView):
......@@ -217,7 +218,7 @@ class ModelView(BaseModelView):
return isinstance(filter, filters.BasePeeweeFilter)
def scaffold_form(self):
form_class = get_form(self.model, self.model_form_converter(),
form_class = get_form(self.model, self.model_form_converter(self),
base_class=self.form_base_class,
only=self.form_columns,
exclude=self.form_excluded_columns,
......@@ -230,7 +231,7 @@ class ModelView(BaseModelView):
return form_class
def scaffold_inline_form_models(self, form_class):
converter = self.model_form_converter()
converter = self.model_form_converter(self)
inline_converter = self.inline_model_form_converter(self)
for m in self.inline_models:
......@@ -241,6 +242,30 @@ class ModelView(BaseModelView):
return form_class
# AJAX foreignkey support
def _create_ajax_loader(self, name, fields):
prop = getattr(self.model, name, None)
if prop is None:
raise ValueError('Model %s does not have field %s.' % (self.model, name))
# TODO: Check for field
remote_model = prop.rel_model
remote_fields = []
for field in fields:
if isinstance(field, string_types):
attr = getattr(remote_model, field, None)
if not attr:
raise ValueError('%s.%s does not exist.' % (remote_model, field))
remote_fields.append(attr)
else:
remote_fields.append(field)
return QueryAjaxModelLoader(name, remote_model, remote_fields)
def _handle_join(self, query, field, joins):
if field.model_class != self.model:
model_name = field.model_class.__name__
......
......@@ -36,7 +36,7 @@ class QueryAjaxModelLoader(AjaxModelLoader):
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
query = self.session.query(self.model)
filters = (field.like('%%%s%%' % term) for field in self.fields)
filters = (field.like(u'%%%s%%' % term) for field in self.fields)
query = query.filter(or_(*filters))
return query.offset(offset).limit(limit).all()
......@@ -2,7 +2,7 @@ from nose.tools import eq_, ok_
from nose.plugins.skip import SkipTest
# Skip test on PY3
from flask.ext.admin._compat import PY2
from flask.ext.admin._compat import PY2, as_unicode
if not PY2:
raise SkipTest('Peewee is not Python 3 compatible')
......@@ -194,3 +194,80 @@ def test_custom_form_base():
create_form = view.create_form()
ok_(isinstance(create_form, TestForm))
def test_ajax_fk():
app, db, admin = setup()
class BaseModel(peewee.Model):
class Meta:
database = db
class Model1(BaseModel):
test1 = peewee.CharField(max_length=20)
test2 = peewee.CharField(max_length=20)
def __str__(self):
return self.test1
class Model2(BaseModel):
model1 = peewee.ForeignKeyField(Model1)
Model1.create_table()
Model2.create_table()
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', test2=u'')
model.save()
model2 = Model1(test1=u'foo', test2=u'bar')
model2.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="[%s, "first"]"' % 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.select().first()
ok_(mdl is not None)
ok_(mdl.model1 is not None)
eq_(mdl.model1.id, model.id)
eq_(mdl.model1.test1, u'first')
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