from ferris.core import inflector, autoadmin
from ferris.core.forms import model_form
from ferris.components.flash_messages import FlashMessages
(autoadmin) # load autoadmin here, if any controller use scaffold it'll be included and initialized
[docs]class Scaffolding(object):
"""
Scaffolding Component
"""
def __init__(self, controller):
self.controller = controller
self._init_meta()
self._init_flash()
def _init_flash(self):
if not FlashMessages in self.controller.Meta.components:
self.controller.components['flash_messages'] = FlashMessages(self.controller)
def _init_meta(self):
"""
Constructs the controller's scaffold property from the controller's Scaffold class.
If the controller doens't have a scaffold, uses the automatic one.
"""
if not hasattr(self.controller.Meta, 'Model'):
_load_model(self.controller)
if not hasattr(self.controller, 'Scaffold'):
setattr(self.controller, 'Scaffold', Scaffold)
if not issubclass(self.controller.Scaffold, Scaffold):
self.controller.Scaffold = type('Scaffold', (self.controller.Scaffold, Scaffold), {})
setattr(self.controller, 'scaffold', self.controller.Scaffold(self.controller))
self.controller.events.template_names += self._on_template_names
self.controller.events.before_render += self._on_before_render
self.controller.events.scaffold_before_parse += self._on_scaffold_before_parse
def _on_scaffold_before_parse(self, controller):
if not hasattr(controller.meta, 'Form') or not controller.meta.Form:
controller.meta.Form = controller.scaffold.ModelForm
def _on_template_names(self, controller, templates):
"""Injects scaffold templates into the template list"""
controller, prefix, action, ext = self.controller.route.name, self.controller.route.prefix, self.controller.route.action, self.controller.meta.view.template_ext
# Try the prefix template first
if prefix:
templates.append('scaffolding/%s_%s.%s' % (prefix, action, ext))
# Then try the non-prefix one.
templates.append('scaffolding/%s.%s' % (action, ext))
def _on_before_render(self, controller):
controller.context['scaffolding'] = {
'name': controller.name,
'proper_name': controller.proper_name,
'title': controller.scaffold.title,
'plural': controller.scaffold.plural,
'singular': controller.scaffold.singular,
'form_action': controller.scaffold.form_action,
'form_encoding': controller.scaffold.form_encoding,
'display_properties': controller.scaffold.display_properties,
'layouts': controller.scaffold.layouts,
'navigation': controller.scaffold.navigation
}
class Scaffold(object):
"""
Scaffold Meta Object Base Class
"""
def __init__(self, controller):
defaults = dict(
query_factory=default_query_factory,
create_factory=default_create_factory,
title=inflector.titleize(controller.proper_name),
plural=inflector.underscore(controller.name),
singular=inflector.underscore(inflector.singularize(controller.name)),
ModelForm=model_form(controller.meta.Model),
display_properties=sorted([name for name, property in controller.meta.Model._properties.items()]),
redirect=controller.uri(action='list') if controller.uri_exists(action='list') else None,
form_action=None,
form_encoding='application/x-www-form-urlencoded',
flash_messages=True,
layouts={
None: 'layouts/default.html',
'admin': 'layouts/admin.html'
},
navigation={}
)
for k, v in defaults.iteritems():
if not hasattr(self, k):
setattr(self, k, v)
# Default Factories
def default_query_factory(controller):
"""
The default factory just returns Model.query(), sorted by created if it's available.
"""
Model = controller.meta.Model
query = Model.query()
if 'created' in Model._properties and Model._properties['created']._indexed:
query = query.order(-Model.created)
return query
def default_create_factory(controller):
"""
The default create factory just calls Model()
"""
return controller.meta.Model()
def delegate_query_factory(controller):
"""
Calls Model.Meta.query_factory or Model.list or Model.query.
"""
Model = controller.Meta.Model
if hasattr(Model.Meta, 'query_factory'):
return Model.Meta.query_factory(controller)
if hasattr(Model, 'list'):
return Model.list(controller)
return default_query_factory(controller)
def delegate_create_factory(controller):
"""
Calls Model.Meta.create_factory or Model.create or the Model constructor.
"""
Model = controller.Meta.Model
if hasattr(Model.Meta, 'create_factory'):
return Model.Meta.create_factory(controller)
if hasattr(Model, 'create'):
return Model.create(controller)
return default_create_factory(controller)
# Utility Functions
def _load_model(controller):
import_form_base = '.'.join(controller.__module__.split('.')[:-2])
# Attempt to import the model automatically
model_name = inflector.singularize(controller.__class__.__name__)
try:
module = __import__('%s.models.%s' % (import_form_base, inflector.underscore(model_name)), fromlist=['*'])
setattr(controller.Meta, 'Model', getattr(module, model_name))
except (ImportError, AttributeError):
raise RuntimeError("Scaffold coudn't automatically determine a model class for controller %s, please assign it a Meta.Model class variable." % controller.__class__.__name__)
def _flash(controller, *args, **kwargs):
if 'flash_messages' in controller.components and controller.scaffold.flash_messages:
controller.components.flash_messages(*args, **kwargs)
# controller Methods
[docs]def list(controller):
controller.context.set(**{
controller.scaffold.plural: controller.scaffold.query_factory(controller)
})
[docs]def view(controller, key):
item = controller.util.decode_key(key).get()
if not item:
return 404
controller.context.set(**{
controller.scaffold.singular: item})
def save_callback(controller, item, parser):
parser.update(item)
controller.events.scaffold_before_save(controller=controller, container=parser.container, item=item)
item.put()
controller.events.scaffold_after_save(controller=controller, container=parser.container, item=item)
def parser_action(controller, item, callback=save_callback):
controller.events.scaffold_before_parse(controller=controller)
parser = controller.parse_request(fallback=item)
if controller.request.method in ('PUT', 'POST', 'PATCH'):
if parser.validate():
controller.events.scaffold_before_apply(controller=controller, container=parser.container, item=item)
callback(controller, item, parser)
controller.events.scaffold_after_apply(controller=controller, container=parser.container, item=item)
controller.context.set(**{
controller.scaffold.singular: item})
_flash(controller, 'The item was saved successfully', 'success')
if controller.scaffold.redirect:
return controller.redirect(controller.scaffold.redirect)
else:
controller.context['errors'] = parser.errors
_flash(controller, 'There were errors on the form, please correct and try again.', 'error')
controller.context.set(**{
'form': parser.container,
controller.scaffold.singular: item})
[docs]def add(controller):
item = controller.scaffold.create_factory(controller)
return parser_action(controller, item)
[docs]def edit(controller, key):
item = controller.util.decode_key(key).get()
if not item:
return 404
return parser_action(controller, item)
[docs]def delete(controller, key):
key = controller.util.decode_key(key)
controller.events.scaffold_before_delete(controller=controller, key=key)
key.delete()
controller.events.scaffold_after_delete(controller=controller, key=key)
_flash(controller, 'The item was deleted successfully', 'success')
if controller.scaffold.redirect:
return controller.redirect(controller.scaffold.redirect)