Remove all old-style CRUD views

This commit is contained in:
Lance Edgar 2017-07-06 15:46:05 -05:00
parent 53d69acbcc
commit 91a14c81a9
3 changed files with 1 additions and 280 deletions

View file

@ -31,7 +31,6 @@ from .master import MasterView
# TODO: deprecate / remove some of this # TODO: deprecate / remove some of this
from .autocomplete import AutocompleteView from .autocomplete import AutocompleteView
from .crud import CrudView
def includeme(config): def includeme(config):

View file

@ -51,7 +51,7 @@ from webhelpers.html import HTML, tags
from tailbone import forms, newgrids as grids from tailbone import forms, newgrids as grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView, CrudView from tailbone.views import MasterView
from tailbone.forms.renderers.batch import FileFieldRenderer from tailbone.forms.renderers.batch import FileFieldRenderer
from tailbone.progress import SessionProgress from tailbone.progress import SessionProgress

View file

@ -1,278 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CRUD View
"""
from __future__ import unicode_literals, absolute_import
try:
from sqlalchemy.inspection import inspect
except ImportError:
inspect = None
from sqlalchemy.orm import class_mapper
import sqlalchemy as sa
from sqlalchemy_continuum import transaction_class, version_class
from sqlalchemy_continuum.utils import is_versioned
from rattail.db import model
from rattail.db.continuum import count_versions, model_transaction_query
from rattail.util import prettify
from formalchemy import FieldSet
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from tailbone.db import Session
from tailbone.views.core import View
from tailbone.forms import AlchemyForm
class CrudView(View):
readonly = False
allow_successive_creates = False
update_cancel_route = None
child_version_classes = []
@property
def mapped_class(self):
raise NotImplementedError
@property
def pretty_name(self):
return self.mapped_class.__name__
@property
def home_route(self):
raise NotImplementedError
@property
def home_url(self):
return self.request.route_url(self.home_route)
@property
def cancel_route(self):
return self.home_route
@property
def cancel_url(self):
return self.request.route_url(self.cancel_route)
def make_fieldset(self, model, **kwargs):
kwargs.setdefault('session', Session())
kwargs.setdefault('request', self.request)
fieldset = FieldSet(model, **kwargs)
fieldset.prettify = prettify
return fieldset
def fieldset(self, model):
return self.make_fieldset(model)
def make_form(self, model, form_factory=AlchemyForm, **kwargs):
fieldset = self.fieldset(model)
kwargs.setdefault('pretty_name', self.pretty_name)
kwargs.setdefault('action_url', self.request.current_route_url())
if self.updating and self.update_cancel_route:
kwargs.setdefault('cancel_url', self.request.route_url(
self.update_cancel_route, uuid=model.uuid))
else:
kwargs.setdefault('cancel_url', self.cancel_url)
kwargs.setdefault('creating', self.creating)
kwargs.setdefault('updating', self.updating)
kwargs.setdefault('session', Session())
form = form_factory(self.request, fieldset, **kwargs)
if form.creating:
if hasattr(self, 'create_label'):
form.create_label = self.create_label
if self.allow_successive_creates:
form.allow_successive_creates = True
if hasattr(self, 'successive_create_label'):
form.successive_create_label = self.successive_create_label
return form
def form(self, model):
return self.make_form(model)
def save_form(self, form):
form.save()
def crud(self, model, readonly=False):
self.readonly = readonly
if self.readonly:
self.creating = False
self.updating = False
else:
self.creating = model is self.mapped_class
self.updating = not self.creating
result = self.pre_crud(model)
if result is not None:
return result
form = self.form(model)
form.readonly = self.readonly
if not self.readonly and self.request.method == 'POST':
if form.validate():
self.save_form(form)
result = self.post_save(form)
if result is not None:
return result
if form.creating:
self.flash_create(form.fieldset.model)
else:
self.flash_update(form.fieldset.model)
if (form.creating and form.allow_successive_creates
and self.request.params.get('create_and_continue')):
return HTTPFound(location=self.request.current_route_url())
if form.creating:
url = self.post_create_url(form)
else:
url = self.post_update_url(form)
return HTTPFound(location=url)
self.validation_failed(form)
result = self.post_crud(model, form)
if result is not None:
return result
kwargs = self.template_kwargs(form)
kwargs['form'] = form
return kwargs
def pre_crud(self, model):
pass
def post_crud(self, model, form):
pass
def template_kwargs(self, form):
if not form.creating and is_versioned(self.mapped_class):
return {'version_count': self.count_versions()}
return {}
def count_versions(self):
query = self.transaction_query()
return query.count()
def transaction_query(self, parent_class=None, child_classes=None):
uuid = self.request.matchdict['uuid']
if parent_class is None:
parent_class = self.mapped_class
if child_classes is None:
child_classes = self.child_version_classes
return model_transaction_query(Session, uuid, parent_class, child_classes=child_classes)
def post_save(self, form):
pass
def post_save_url(self, form):
return self.home_url
def post_create_url(self, form):
return self.post_save_url(form)
def post_update_url(self, form):
return self.post_save_url(form)
def validation_failed(self, form):
pass
def flash_create(self, model):
self.request.session.flash("%s \"%s\" has been created." %
(self.pretty_name, model))
def flash_delete(self, model):
self.request.session.flash("%s \"%s\" has been deleted." %
(self.pretty_name, model))
def flash_update(self, model):
self.request.session.flash("%s \"%s\" has been updated." %
(self.pretty_name, model))
def create(self):
return self.crud(self.mapped_class)
def get_model_from_request(self):
if inspect:
mapper = inspect(self.mapped_class)
else:
mapper = class_mapper(self.mapped_class)
assert len(mapper.primary_key) == 1
key = self.request.matchdict[mapper.primary_key[0].key]
return self.get_model(key)
def get_model(self, key):
model = Session.query(self.mapped_class).get(key)
return model
def read(self):
model = self.get_model_from_request()
if not model:
return HTTPNotFound()
return self.crud(model, readonly=True)
def update(self):
model = self.get_model_from_request()
if not model:
return HTTPNotFound()
return self.crud(model)
def delete(self):
"""
View for deleting a record. Derived classes shouldn't override this,
but see also :meth:`pre_delete()` and :meth:`post_delete()`.
"""
model = self.get_model_from_request()
if not model:
return HTTPNotFound()
# Let derived classes prep for (or cancel) deletion.
result = self.pre_delete(model)
if result is not None:
return result
# Flush the deletion immediately so that we know it will succeed prior
# to setting a flash message etc.
Session.delete(model)
Session.flush()
# Derived classes can do extra things here; set flash and go home.
self.post_delete(model)
self.flash_delete(model)
return HTTPFound(location=self.home_url)
def pre_delete(self, model):
pass
def post_delete(self, model):
pass