236 lines
6.8 KiB
Python
236 lines
6.8 KiB
Python
import logging
|
|
|
|
from flask_admin.model import BaseModelView
|
|
from wtforms_appengine import db as wt_db
|
|
from wtforms_appengine import ndb as wt_ndb
|
|
|
|
from google.appengine.ext import db
|
|
from google.appengine.ext import ndb
|
|
|
|
from flask_wtf import Form
|
|
from flask_admin.model.form import create_editable_list_form
|
|
from .form import AdminModelConverter
|
|
|
|
|
|
class NdbModelView(BaseModelView):
|
|
"""
|
|
AppEngine NDB model scaffolding.
|
|
"""
|
|
|
|
def get_pk_value(self, model):
|
|
return model.key.urlsafe()
|
|
|
|
def scaffold_list_columns(self):
|
|
return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property)])
|
|
|
|
def scaffold_sortable_columns(self):
|
|
return [k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property) and v._indexed]
|
|
|
|
def init_search(self):
|
|
return None
|
|
|
|
def is_valid_filter(self):
|
|
pass
|
|
|
|
def scaffold_filters(self):
|
|
# TODO: implement
|
|
pass
|
|
|
|
form_args = None
|
|
|
|
model_form_converter = AdminModelConverter
|
|
"""
|
|
Model form conversion class. Use this to implement custom field conversion logic.
|
|
|
|
For example::
|
|
|
|
class MyModelConverter(AdminModelConverter):
|
|
pass
|
|
|
|
|
|
class MyAdminView(ModelView):
|
|
model_form_converter = MyModelConverter
|
|
"""
|
|
|
|
def scaffold_form(self):
|
|
form_class = wt_ndb.model_form(
|
|
self.model(),
|
|
base_class=Form,
|
|
only=self.form_columns,
|
|
exclude=self.form_excluded_columns,
|
|
field_args=self.form_args,
|
|
converter=self.model_form_converter(),
|
|
)
|
|
return form_class
|
|
|
|
def scaffold_list_form(self, widget=None, validators=None):
|
|
form_class = wt_ndb.model_form(
|
|
self.model(),
|
|
base_class=Form,
|
|
only=self.column_editable_list,
|
|
field_args=self.form_args,
|
|
converter=self.model_form_converter(),
|
|
)
|
|
result = create_editable_list_form(Form, form_class, widget)
|
|
return result
|
|
|
|
def get_list(self, page, sort_field, sort_desc, search, filters,
|
|
page_size=None):
|
|
# TODO: implement filters (don't think search can work here)
|
|
|
|
q = self.model.query()
|
|
|
|
if sort_field:
|
|
order_field = getattr(self.model, sort_field)
|
|
if sort_desc:
|
|
order_field = -order_field
|
|
q = q.order(order_field)
|
|
|
|
if not page_size:
|
|
page_size = self.page_size
|
|
|
|
results = q.fetch(page_size, offset=page * page_size)
|
|
|
|
return q.count(), results
|
|
|
|
def get_one(self, urlsafe_key):
|
|
return ndb.Key(urlsafe=urlsafe_key).get()
|
|
|
|
def create_model(self, form):
|
|
try:
|
|
model = self.model()
|
|
form.populate_obj(model)
|
|
model.put()
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to create record. %(error)s',
|
|
# error=ex), 'error')
|
|
logging.exception('Failed to create record.')
|
|
return False
|
|
else:
|
|
self.after_model_change(form, model, True)
|
|
|
|
return model
|
|
|
|
def update_model(self, form, model):
|
|
try:
|
|
form.populate_obj(model)
|
|
model.put()
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to update record. %(error)s',
|
|
# error=ex), 'error')
|
|
logging.exception('Failed to update record.')
|
|
return False
|
|
else:
|
|
self.after_model_change(form, model, False)
|
|
|
|
return True
|
|
|
|
def delete_model(self, model):
|
|
try:
|
|
model.key.delete()
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to delete record. %(error)s',
|
|
# error=ex),
|
|
# 'error')
|
|
logging.exception('Failed to delete record.')
|
|
return False
|
|
else:
|
|
self.after_model_delete(model)
|
|
|
|
return True
|
|
|
|
|
|
class DbModelView(BaseModelView):
|
|
"""
|
|
AppEngine DB model scaffolding.
|
|
"""
|
|
|
|
def get_pk_value(self, model):
|
|
return str(model.key())
|
|
|
|
def scaffold_list_columns(self):
|
|
return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, db.Property)])
|
|
|
|
def scaffold_sortable_columns(self):
|
|
# We use getattr() because ReferenceProperty does not specify a 'indexed' field
|
|
return [k for (k, v) in self.model.__dict__.iteritems()
|
|
if isinstance(v, db.Property) and getattr(v, 'indexed', None)]
|
|
|
|
def init_search(self):
|
|
return None
|
|
|
|
def is_valid_filter(self):
|
|
pass
|
|
|
|
def scaffold_filters(self):
|
|
# TODO: implement
|
|
pass
|
|
|
|
def scaffold_form(self):
|
|
return wt_db.model_form(self.model())
|
|
|
|
def get_list(self, page, sort_field, sort_desc, search, filters):
|
|
# TODO: implement filters (don't think search can work here)
|
|
|
|
q = self.model.all()
|
|
|
|
if sort_field:
|
|
if sort_desc:
|
|
sort_field = "-" + sort_field
|
|
q.order(sort_field)
|
|
|
|
results = q.fetch(self.page_size, offset=page * self.page_size)
|
|
return q.count(), results
|
|
|
|
def get_one(self, encoded_key):
|
|
return db.get(db.Key(encoded=encoded_key))
|
|
|
|
def create_model(self, form):
|
|
try:
|
|
model = self.model()
|
|
form.populate_obj(model)
|
|
model.put()
|
|
return model
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to create record. %(error)s',
|
|
# error=ex), 'error')
|
|
logging.exception('Failed to create record.')
|
|
return False
|
|
|
|
def update_model(self, form, model):
|
|
try:
|
|
form.populate_obj(model)
|
|
model.put()
|
|
return True
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to update record. %(error)s',
|
|
# error=ex), 'error')
|
|
logging.exception('Failed to update record.')
|
|
return False
|
|
|
|
def delete_model(self, model):
|
|
try:
|
|
model.delete()
|
|
return True
|
|
except Exception as ex:
|
|
if not self.handle_view_exception(ex):
|
|
# flash(gettext('Failed to delete record. %(error)s',
|
|
# error=ex),
|
|
# 'error')
|
|
logging.exception('Failed to delete record.')
|
|
return False
|
|
|
|
|
|
def ModelView(model):
|
|
if issubclass(model, ndb.Model):
|
|
return NdbModelView(model)
|
|
elif issubclass(model, db.Model):
|
|
return DbModelView(model)
|
|
else:
|
|
raise ValueError("Unsupported model: %s" % model)
|