Commit 572f1d39 authored by Paul Brown's avatar Paul Brown

Merge pull request #1156 from mikelambert/master

Add a demo app for appengine (and fix one more bug)
parents 1595568e 9fba3f89
...@@ -19,6 +19,7 @@ __pycache__ ...@@ -19,6 +19,7 @@ __pycache__
examples/sqla-inline/static examples/sqla-inline/static
examples/file/files examples/file/files
examples/forms/files examples/forms/files
examples/appengine/lib
.DS_Store .DS_Store
.idea/ .idea/
*.sqlite *.sqlite
......
from flask import Flask
import flask_admin
from flask_admin.contrib import appengine
from google.appengine.ext import db
from google.appengine.ext import ndb
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
admin = flask_admin.Admin(app, name="Admin")
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
class Car(db.Model):
owner = db.StringProperty()
make_model = db.StringProperty()
indexed_int = db.IntegerProperty()
unindexed_int = db.IntegerProperty(indexed=False)
class Tire(db.Model):
car = db.ReferenceProperty(Car)
position = db.StringProperty()
class House(ndb.Model):
address = db.StringProperty()
json_property = ndb.JsonProperty()
indexed_int = ndb.IntegerProperty()
unindexed_int = ndb.IntegerProperty(indexed=False)
text_property = ndb.TextProperty()
datetime_property = ndb.DateTimeProperty()
class Room(ndb.Model):
house = ndb.KeyProperty(kind=House)
name = ndb.StringProperty()
admin.add_view(appengine.ModelView(Car))
admin.add_view(appengine.ModelView(Tire))
admin.add_view(appengine.ModelView(House))
admin.add_view(appengine.ModelView(Room))
if __name__ == '__main__':
# Start app
app.run(debug=True)
runtime: python27
threadsafe: true
api_version: 1
module: default
handlers:
- url: /admin.*
script: app.app
# This file gets imported as part of running dev_appserver.py
import sys
sys.path = ['lib'] + sys.path
BASEDIR=$(dirname $0)
# Install wtforms-admin to our lib/ directory, using our local source tree
pip install -t $BASEDIR/lib/ $BASEDIR/../.. wtforms_appengine
# Now run our server
dev_appserver.py $BASEDIR/app.yaml
...@@ -8,171 +8,172 @@ from google.appengine.ext import db ...@@ -8,171 +8,172 @@ from google.appengine.ext import db
from google.appengine.ext import ndb from google.appengine.ext import ndb
class NdbModelView(BaseModelView): class NdbModelView(BaseModelView):
""" """
AppEngine NDB model scaffolding. AppEngine NDB model scaffolding.
""" """
def get_pk_value(self, model): def get_pk_value(self, model):
return model.key.urlsafe() return model.key.urlsafe()
def scaffold_list_columns(self): def scaffold_list_columns(self):
return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property)]) return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property)])
def scaffold_sortable_columns(self): def scaffold_sortable_columns(self):
return [k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property) and v._indexed] return [k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, ndb.Property) and v._indexed]
def init_search(self): def init_search(self):
return None return None
def is_valid_filter(self): def is_valid_filter(self):
pass pass
def scaffold_filters(self): def scaffold_filters(self):
#TODO: implement #TODO: implement
pass pass
def scaffold_form(self): def scaffold_form(self):
return wt_ndb.model_form(self.model()) return wt_ndb.model_form(self.model())
def get_list(self, page, sort_field, sort_desc, search, filters): def get_list(self, page, sort_field, sort_desc, search, filters):
#TODO: implement filters (don't think search can work here) #TODO: implement filters (don't think search can work here)
q = self.model.query() q = self.model.query()
if sort_field: if sort_field:
order_field = getattr(self.model, sort_field) order_field = getattr(self.model, sort_field)
if sort_desc: if sort_desc:
order_field = -order_field order_field = -order_field
q = q.order(order_field) q = q.order(order_field)
results = q.fetch(self.page_size, offset=page*self.page_size) results = q.fetch(self.page_size, offset=page*self.page_size)
return q.count(), results return q.count(), results
def get_one(self, urlsafe_key): def get_one(self, urlsafe_key):
return ndb.Key(urlsafe=urlsafe_key).get() return ndb.Key(urlsafe=urlsafe_key).get()
def create_model(self, form): def create_model(self, form):
try: try:
model = self.model() model = self.model()
form.populate_obj(model) form.populate_obj(model)
model.put() model.put()
return model return model
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
#flash(gettext('Failed to create record. %(error)s', #flash(gettext('Failed to create record. %(error)s',
# error=ex), 'error') # error=ex), 'error')
logging.exception('Failed to create record.') logging.exception('Failed to create record.')
return False return False
def update_model(self, form, model): def update_model(self, form, model):
try: try:
form.populate_obj(model) form.populate_obj(model)
model.put() model.put()
return True return True
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
#flash(gettext('Failed to update record. %(error)s', #flash(gettext('Failed to update record. %(error)s',
# error=ex), 'error') # error=ex), 'error')
logging.exception('Failed to update record.') logging.exception('Failed to update record.')
return False return False
def delete_model(self, model): def delete_model(self, model):
try: try:
model.key.delete() model.key.delete()
return True return True
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
#flash(gettext('Failed to delete record. %(error)s', #flash(gettext('Failed to delete record. %(error)s',
# error=ex), # error=ex),
# 'error') # 'error')
logging.exception('Failed to delete record.') logging.exception('Failed to delete record.')
return False return False
class DbModelView(BaseModelView): class DbModelView(BaseModelView):
""" """
AppEngine DB model scaffolding. AppEngine DB model scaffolding.
""" """
def get_pk_value(self, model): def get_pk_value(self, model):
return str(model.key()) return str(model.key())
def scaffold_list_columns(self): def scaffold_list_columns(self):
return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, db.Property)]) return sorted([k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, db.Property)])
def scaffold_sortable_columns(self): def scaffold_sortable_columns(self):
return [k for (k, v) in self.model.__dict__.iteritems() if isinstance(v, db.Property) and v.indexed] # 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 init_search(self):
return None
def is_valid_filter(self):
pass def is_valid_filter(self):
pass
def scaffold_filters(self):
#TODO: implement def scaffold_filters(self):
pass #TODO: implement
pass
def scaffold_form(self):
return wt_db.model_form(self.model()) 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) 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()
q = self.model.all()
if sort_field:
if sort_desc: if sort_field:
sort_field = "-" + sort_field if sort_desc:
q.order(sort_field) sort_field = "-" + sort_field
q.order(sort_field)
results = q.fetch(self.page_size, offset=page*self.page_size)
return q.count(), results 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 get_one(self, encoded_key):
return db.get(db.Key(encoded=encoded_key))
def create_model(self, form):
try: def create_model(self, form):
model = self.model() try:
form.populate_obj(model) model = self.model()
model.put() form.populate_obj(model)
return model model.put()
except Exception as ex: return model
if not self.handle_view_exception(ex): except Exception as ex:
#flash(gettext('Failed to create record. %(error)s', if not self.handle_view_exception(ex):
# error=ex), 'error') #flash(gettext('Failed to create record. %(error)s',
logging.exception('Failed to create record.') # error=ex), 'error')
return False logging.exception('Failed to create record.')
return False
def update_model(self, form, model):
try: def update_model(self, form, model):
form.populate_obj(model) try:
model.put() form.populate_obj(model)
return True model.put()
except Exception as ex: return True
if not self.handle_view_exception(ex): except Exception as ex:
#flash(gettext('Failed to update record. %(error)s', if not self.handle_view_exception(ex):
# error=ex), 'error') #flash(gettext('Failed to update record. %(error)s',
logging.exception('Failed to update record.') # error=ex), 'error')
return False logging.exception('Failed to update record.')
return False
def delete_model(self, model):
try: def delete_model(self, model):
model.delete() try:
return True model.delete()
except Exception as ex: return True
if not self.handle_view_exception(ex): except Exception as ex:
#flash(gettext('Failed to delete record. %(error)s', if not self.handle_view_exception(ex):
# error=ex), #flash(gettext('Failed to delete record. %(error)s',
# 'error') # error=ex),
logging.exception('Failed to delete record.') # 'error')
return False logging.exception('Failed to delete record.')
return False
def ModelView(model): def ModelView(model):
if issubclass(model, ndb.Model): if issubclass(model, ndb.Model):
return NdbModelView(model) return NdbModelView(model)
elif issubclass(model, db.Model): elif issubclass(model, db.Model):
return DbModelView(model) return DbModelView(model)
else: else:
raise ValueError("Unsupported model: %s" % model) raise ValueError("Unsupported model: %s" % model)
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