これは、閲覧専用フォームと編集フォームの両方を含む、150 行の回答です。最も単純なユースケースでは不必要に複雑かもしれませんが、それが機能するようになるまでには時間がかかりました.
class FormView:
"""A base class for views which utilize ColanderAlchemy to view/edit SQLAlchemy model instances."""
#: If the child class is not overriding the rendering loop, point this to a template which provides the page frame and ``crud_content`` block.
base_template = None
#: List of SQLAlchemy and JSONProperty field names automatically mapped to a form
includes = ["id",]
def __init__(self, context, request):
self.context = context
self.request = request
def create_form(self, buttons=()):
"""Automatically create a read-only collander schema + deform form based on the underlying SQLALchemy model.
:param buttons: Passed to Deform as form buttons
"""
obj = self.get_object()
includes = self.includes
schema = PropertyAwareSQLAlchemySchemaNode(obj.__class__, includes=includes)
self.customize_schema(schema)
schema = self.bind_schema(schema)
form = deform.Form(schema, buttons=buttons)
return form
def bind_schema(self, schema):
"""Initialize Colander field dynamic default values. By default, don't do anything."""
return schema
def get_crud(self):
"""Get CRUD manager object for this view."""
return self.context.__parent__
def get_object(self):
"""Get underlying SQLAlchemy model instance from current Pyramid traversing context."""
return self.context.get_object()
def get_title(self):
"""Get human-readable title for for template page title."""
return "#{}".format(self.get_object().id)
def customize_schema(self, schema):
"""After Colander schema is automatically generated from the SQLAlchemy model, edit it in-place for fine-tuning.
Override this in your view subclass for schema customizations.
"""
return
class Show(FormView):
"""Read-only view to SQLAlchemy model instance using Deform form generated by ColanderAlchemy.
"""
def get_title(self):
return "#{}".format(self.get_object().id)
@view_config(context=sqlalchemy.Resource, name="show", renderer="crud/show.html", permission='view')
def show(self):
"""View for showing an individual object."""
obj = self.context.get_object()
base_template = self.base_template
form = self.create_form()
appstruct = form.schema.dictify(obj)
rendered_form = form.render(appstruct, readonly=True)
crud = self.get_crud()
resource_buttons = dict(edit=self.request.resource_url(self.context, "edit"), delete=False)
title = current_view_name = self.get_title()
return dict(form=rendered_form, context=self.context, obj=obj, title=title, crud=crud, base_template=base_template, resource_buttons=resource_buttons)
class Edit(FormView):
"""Edit SQLAlchemy model instance using Deform form generated by ColanderAlchemy.
"""
# We display id field on the edit form and it needs special handling, because it is read-only
# See http://deformdemo.repoze.org/readonly_value_nonvalidation/
includes = [
colander.SchemaNode(colander.String(),
name="id",
missing=lambda node, kw: kw["obj"].id,
widget=deform.widget.TextInputWidget(readonly=True),
)
]
def get_title(self):
return "Editing #{}".format(self.get_object().id)
def create_form(self):
return super(Edit, self).create_form(buttons=("save", "cancel",))
def bind_schema(self, schema):
return schema.bind(obj=self.context.get_object())
@view_config(context=sqlalchemy.Resource, name="edit", renderer="crud/edit.html", permission='edit')
def edit(self):
"""View for showing an individual object."""
# SQLAlchemy model instance
obj = self.context.get_object()
base_template = self.base_template
# Create form, convert instance to Colander structure for Deform
form = self.create_form()
crud = self.get_crud()
resource_buttons = dict(show=self.request.resource_url(self.context, "show"), delete=False)
title = current_view_name = self.get_title()
if "save" in self.request.POST:
controls = self.request.POST.items()
try:
appstruct = form.validate(controls)
# Cannot update id, as it is read-only
del appstruct["id"]
form.schema.objectify(appstruct, obj)
# We do not need to explicitly call save() or commit() as we are using Zope transaction manager
messages.add(self.request, kind="success", msg="Changes saved.")
# Redirect back to view page after edit page has succeeded
return HTTPFound(self.request.resource_url(self.context, "show"))
except deform.ValidationFailure as e:
# Whoops, bad things happened, render form with validation errors
rendered_form = e.render()
elif "cancel" in self.request.POST:
# User pressed cancel
return HTTPFound(self.request.resource_url(self.context, "show"))
else:
# Render initial form view with populated values
appstruct = form.schema.dictify(obj)
rendered_form = form.render(appstruct)
return dict(form=rendered_form, context=self.context, obj=obj, title=title, crud=crud, base_template=base_template, resource_buttons=resource_buttons)