Add master4, refactor customers view to use it

This commit is contained in:
Lance Edgar 2018-02-03 16:12:36 -06:00
parent ab16ffc823
commit 63290154eb
6 changed files with 348 additions and 18 deletions

View file

@ -1,4 +1,6 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/mobile/master/create.mako" /> <%inherit file="/mobile/master/create.mako" />
<%def name="title()">New ${model_title} Row</%def>
${parent.body()} ${parent.body()}

View file

@ -8,5 +8,5 @@
${parent.body()} ${parent.body()}
% if master.mobile_rows_editable and instance_editable and request.has_perm('{}.edit_row'.format(permission_prefix)): % if master.mobile_rows_editable and instance_editable and request.has_perm('{}.edit_row'.format(permission_prefix)):
${h.link_to("Edit", url('mobile.{}.edit_row'.format(route_prefix), uuid=instance.uuid), class_='ui-btn')} ${h.link_to("Edit", url('mobile.{}.edit_row'.format(route_prefix), uuid=instance.batch_uuid, row_uuid=instance.uuid), class_='ui-btn')}
% endif % endif

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2018 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -30,6 +30,7 @@ from .core import View
from .master import MasterView from .master import MasterView
from .master2 import MasterView2 from .master2 import MasterView2
from .master3 import MasterView3 from .master3 import MasterView3
from .master4 import MasterView4
# TODO: deprecate / remove some of this # TODO: deprecate / remove some of this
from .autocomplete import AutocompleteView from .autocomplete import AutocompleteView

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2018 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -39,7 +39,7 @@ from webhelpers2.html import HTML, tags
from tailbone import grids from tailbone import grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView3 as MasterView, AutocompleteView from tailbone.views import MasterView4 as MasterView, AutocompleteView
from rattail.db import model from rattail.db import model
@ -83,6 +83,19 @@ class CustomersView(MasterView):
'groups', 'groups',
] ]
mobile_form_fields = [
'id',
'name',
'default_phone',
'default_email',
'default_address',
'email_preference',
'active_in_pos',
'active_in_pos_sticky',
'people',
'groups',
]
def configure_grid(self, g): def configure_grid(self, g):
super(CustomersView, self).configure_grid(g) super(CustomersView, self).configure_grid(g)
@ -286,13 +299,6 @@ class CustomersView(MasterView):
items.append(HTML.tag('li', tags.link_to(text, url))) items.append(HTML.tag('li', tags.link_to(text, url)))
return HTML.tag('ul', HTML.literal('').join(items)) return HTML.tag('ul', HTML.literal('').join(items))
# def configure_mobile_fieldset(self, fs):
# fs.configure(
# include=[
# fs.email,
# fs.phone,
# ])
def get_version_child_classes(self): def get_version_child_classes(self):
return [ return [
(model.CustomerPhoneNumber, 'parent_uuid'), (model.CustomerPhoneNumber, 'parent_uuid'),

View file

@ -824,6 +824,9 @@ class MasterView(View):
def validate_mobile_form(self, form): def validate_mobile_form(self, form):
return form.validate() return form.validate()
def validate_row_form(self, form):
return form.validate()
def save_edit_form(self, form): def save_edit_form(self, form):
self.save_form(form) self.save_form(form)
self.after_edit(form.fieldset.model) self.after_edit(form.fieldset.model)
@ -1741,10 +1744,9 @@ class MasterView(View):
index_url = self.get_action_url('view', parent) index_url = self.get_action_url('view', parent)
form = self.make_row_form(self.model_row_class, cancel_url=index_url) form = self.make_row_form(self.model_row_class, cancel_url=index_url)
if self.request.method == 'POST': if self.request.method == 'POST':
if form.validate(): if self.validate_row_form(form):
self.before_create_row(form) self.before_create_row(form)
self.save_create_row_form(form) obj = self.save_create_row_form(form) or form.fieldset.model
obj = form.fieldset.model
self.after_create_row(obj) self.after_create_row(obj)
return self.redirect_after_create_row(obj) return self.redirect_after_create_row(obj)
return self.render_to_response('create_row', { return self.render_to_response('create_row', {
@ -1775,7 +1777,7 @@ class MasterView(View):
instance_url = self.get_action_url('view', parent, mobile=True) instance_url = self.get_action_url('view', parent, mobile=True)
form = self.make_mobile_row_form(self.model_row_class, cancel_url=instance_url) form = self.make_mobile_row_form(self.model_row_class, cancel_url=instance_url)
if self.request.method == 'POST': if self.request.method == 'POST':
if form.validate(): if self.validate_mobile_row_form(form):
self.before_create_row(form) self.before_create_row(form)
# let save() return alternate object if necessary # let save() return alternate object if necessary
obj = self.save_create_row_form(form) or form.fieldset.model obj = self.save_create_row_form(form) or form.fieldset.model
@ -1835,7 +1837,7 @@ class MasterView(View):
form = self.make_row_form(row) form = self.make_row_form(row)
if self.request.method == 'POST': if self.request.method == 'POST':
if form.validate(): if self.validate_row_form(form):
self.save_edit_row_form(form) self.save_edit_row_form(form)
return self.redirect_after_edit_row(row) return self.redirect_after_edit_row(row)
@ -1860,7 +1862,7 @@ class MasterView(View):
form = self.make_mobile_row_form(row) form = self.make_mobile_row_form(row)
if self.request.method == 'POST': if self.request.method == 'POST':
if form.validate(): if self.validate_mobile_row_form(form):
self.save_edit_row_form(form) self.save_edit_row_form(form)
return self.redirect_after_edit_row(row, mobile=True) return self.redirect_after_edit_row(row, mobile=True)

319
tailbone/views/master4.py Normal file
View file

@ -0,0 +1,319 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2018 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 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 General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Master View (v4)
"""
from __future__ import unicode_literals, absolute_import
import deform
from tailbone import forms2 as forms
from tailbone.views import MasterView3
class MasterView4(MasterView3):
"""
Base "master" view class. All model master views should derive from this.
"""
row_labels = {}
def make_mobile_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a new mobile form for the given model class/instance.
"""
if factory is None:
factory = self.get_mobile_form_factory()
if fields is None:
fields = self.get_mobile_form_fields()
if schema is None:
schema = self.make_mobile_form_schema()
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_mobile_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_mobile_form(form)
return form
def make_row_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a new row form for the given model class/instance.
"""
if factory is None:
factory = self.get_row_form_factory()
if fields is None:
fields = self.get_row_form_fields()
if schema is None:
schema = self.make_row_form_schema()
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_row_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_row_form(form)
return form
def make_mobile_row_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a new mobile form for the given model class/instance.
"""
if factory is None:
factory = self.get_mobile_row_form_factory()
if fields is None:
fields = self.get_mobile_row_form_fields()
if schema is None:
schema = self.make_mobile_row_form_schema()
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_mobile_row_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_mobile_row_form(form)
return form
@classmethod
def get_mobile_form_factory(cls):
"""
Returns the factory or class which is to be used when creating new
mobile forms.
"""
return getattr(cls, 'mobile_form_factory', forms.Form)
@classmethod
def get_row_form_factory(cls):
"""
Returns the factory or class which is to be used when creating new row
forms.
"""
return getattr(cls, 'row_form_factory', forms.Form)
@classmethod
def get_mobile_row_form_factory(cls):
"""
Returns the factory or class which is to be used when creating new
mobile row forms.
"""
return getattr(cls, 'mobile_row_form_factory', forms.Form)
def make_mobile_form_schema(self):
if not self.model_class:
# TODO
raise NotImplementedError
def make_row_form_schema(self):
if not self.model_row_class:
# TODO
raise NotImplementedError
def make_mobile_row_form_schema(self):
if not self.model_row_class:
# TODO
raise NotImplementedError
def get_mobile_form_fields(self):
if hasattr(self, 'mobile_form_fields'):
return self.mobile_form_fields
# TODO
# raise NotImplementedError
def get_row_form_fields(self):
if hasattr(self, 'row_form_fields'):
return self.row_form_fields
# TODO
# raise NotImplementedError
def get_mobile_row_form_fields(self):
if hasattr(self, 'mobile_row_form_fields'):
return self.mobile_row_form_fields
# TODO
# raise NotImplementedError
def make_mobile_form_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new mobile forms.
"""
defaults = {
'request': self.request,
'readonly': self.viewing,
'model_class': getattr(self, 'model_class', None),
'action_url': self.request.current_route_url(_query=None),
}
if self.creating:
defaults['cancel_url'] = self.get_index_url(mobile=True)
else:
instance = kwargs['model_instance']
defaults['cancel_url'] = self.get_action_url('view', instance, mobile=True)
defaults.update(kwargs)
return defaults
def make_row_form_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new row forms.
"""
defaults = {
'request': self.request,
'readonly': self.viewing,
'model_class': getattr(self, 'model_row_class', None),
'action_url': self.request.current_route_url(_query=None),
}
if self.creating:
kwargs.setdefault('cancel_url', self.request.get_referrer())
else:
instance = kwargs['model_instance']
kwargs.setdefault('cancel_url', self.get_row_action_url('view', instance))
defaults.update(kwargs)
return defaults
def make_mobile_row_form_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new mobile row forms.
"""
defaults = {
'request': self.request,
'readonly': self.viewing,
'model_class': getattr(self, 'model_row_class', None),
'action_url': self.request.current_route_url(_query=None),
}
if self.creating:
defaults['cancel_url'] = self.request.get_referrer()
else:
instance = kwargs['model_instance']
defaults['cancel_url'] = self.get_row_action_url('view', instance, mobile=True)
defaults.update(kwargs)
return defaults
def configure_mobile_form(self, form):
"""
Configure the primary mobile form.
"""
# TODO: is any of this stuff from configure_form() needed?
# if self.editing:
# model_class = self.get_model_class(error=False)
# if model_class:
# mapper = orm.class_mapper(model_class)
# for key in mapper.primary_key:
# for field in form.fields:
# if field == key.name:
# form.set_readonly(field)
# break
# form.remove_field('uuid')
self.set_labels(form)
def configure_row_grid(self, grid):
super(MasterView4, self).configure_row_grid(grid)
self.set_row_labels(grid)
def configure_row_form(self, form):
"""
Configure a row form.
"""
# TODO: is any of this stuff from configure_form() needed?
# if self.editing:
# model_class = self.get_model_class(error=False)
# if model_class:
# mapper = orm.class_mapper(model_class)
# for key in mapper.primary_key:
# for field in form.fields:
# if field == key.name:
# form.set_readonly(field)
# break
# form.remove_field('uuid')
self.set_row_labels(form)
def configure_mobile_row_form(self, form):
"""
Configure the mobile row form.
"""
# TODO: is any of this stuff from configure_form() needed?
# if self.editing:
# model_class = self.get_model_class(error=False)
# if model_class:
# mapper = orm.class_mapper(model_class)
# for key in mapper.primary_key:
# for field in form.fields:
# if field == key.name:
# form.set_readonly(field)
# break
# form.remove_field('uuid')
self.set_row_labels(form)
def set_row_labels(self, obj):
for key, label in self.row_labels.items():
obj.set_label(key, label)
def validate_mobile_form(self, form):
controls = self.request.POST.items()
try:
self.form_deserialized = form.validate(controls)
except deform.ValidationFailure:
return False
return True
def validate_row_form(self, form):
controls = self.request.POST.items()
try:
self.form_deserialized = form.validate(controls)
except deform.ValidationFailure:
return False
return True
def validate_mobile_row_form(self, form):
controls = self.request.POST.items()
try:
self.form_deserialized = form.validate(controls)
except deform.ValidationFailure:
return False
return True
def save_mobile_create_form(self, form):
self.before_create(form)
with self.Session.no_autoflush:
obj = self.objectify(form, self.form_deserialized)
self.before_create_flush(obj, form)
self.Session.add(obj)
self.Session.flush()
return obj
# TODO: still need to verify this logic
def save_create_row_form(self, form):
# self.before_create(form)
# with self.Session().no_autoflush:
# obj = self.objectify(form, self.form_deserialized)
# self.before_create_flush(obj, form)
obj = self.objectify(form, self.form_deserialized)
self.Session.add(obj)
self.Session.flush()
return obj
def save_edit_row_form(self, form):
obj = self.objectify(form, self.form_deserialized)
self.after_edit_row(obj)
self.Session.flush()
return obj