Refactor employees view to use master3

This commit is contained in:
Lance Edgar 2017-12-04 13:48:31 -06:00
parent 7a777964a7
commit 84ebf5d929
4 changed files with 181 additions and 117 deletions

View file

@ -26,15 +26,18 @@ Employee Views
from __future__ import unicode_literals, absolute_import
import six
import sqlalchemy as sa
from rattail.db import model
import formalchemy as fa
import colander
from deform import widget as dfwidget
from webhelpers2.html import tags, HTML
from tailbone import forms, grids
from tailbone import grids
from tailbone.db import Session
from tailbone.views import MasterView2 as MasterView, AutocompleteView
from tailbone.views import MasterView3 as MasterView, AutocompleteView
class EmployeesView(MasterView):
@ -53,6 +56,21 @@ class EmployeesView(MasterView):
'status',
]
form_fields = [
'person',
'first_name',
'last_name',
'display_name',
'phone',
'email',
'status',
'full_time',
'full_time_start',
'id',
'stores',
'departments',
]
def configure_grid(self, g):
super(EmployeesView, self).configure_grid(g)
@ -124,38 +142,116 @@ class EmployeesView(MasterView):
return not bool(employee.user and employee.user.username == 'chuck')
return True
def _preconfigure_fieldset(self, fs):
fs.append(forms.AssociationProxyField('first_name'))
fs.append(forms.AssociationProxyField('last_name'))
fs.append(StoresField('stores'))
fs.append(DepartmentsField('departments'))
def configure_form(self, f):
super(EmployeesView, self).configure_form(f)
employee = f.model_instance
fs.person.set(renderer=forms.renderers.PersonFieldRenderer)
fs.display_name.set(label="Short Name")
fs.phone.set(label="Phone Number", readonly=True)
fs.email.set(label="Email Address", readonly=True)
fs.status.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.EMPLOYEE_STATUS))
fs.id.set(label="ID")
f.set_renderer('person', self.render_person)
f.set_renderer('stores', self.render_stores)
f.set_label('stores', "Stores") # TODO: should not be necessary
if self.creating or self.editing:
stores = self.get_possible_stores().all()
store_values = [(s.uuid, six.text_type(s)) for s in stores]
f.set_node('stores', colander.SchemaNode(colander.Set()))
f.set_widget('stores', dfwidget.SelectWidget(multiple=True,
size=len(stores),
values=store_values))
if self.editing:
f.set_default('stores', [s.uuid for s in employee.stores])
f.set_renderer('departments', self.render_departments)
f.set_label('departments', "Departments") # TODO: should not be necessary
if self.creating or self.editing:
departments = self.get_possible_departments().all()
dept_values = [(d.uuid, six.text_type(d)) for d in departments]
f.set_node('departments', colander.SchemaNode(colander.Set()))
f.set_widget('departments', dfwidget.SelectWidget(multiple=True,
size=len(departments),
values=dept_values))
if self.editing:
f.set_default('departments', [d.uuid for d in employee.departments])
f.set_enum('status', self.enum.EMPLOYEE_STATUS)
f.set_type('full_time_start', 'date_jquery')
if self.editing:
# TODO: this should not be needed (association proxy)
f.set_default('full_time_start', employee.full_time_start)
f.set_readonly('person')
f.set_readonly('phone')
f.set_readonly('email')
f.set_label('display_name', "Short Name")
f.set_label('phone', "Phone Number")
f.set_label('email', "Email Address")
f.set_label('id', "ID")
def configure_fieldset(self, fs):
fs.configure(
include=[
fs.person,
fs.first_name,
fs.last_name,
fs.display_name,
fs.phone,
fs.email,
fs.status,
fs.full_time,
fs.full_time_start,
fs.id,
fs.stores,
fs.departments,
])
if not self.viewing:
del fs.first_name
del fs.last_name
f.remove_fields('first_name', 'last_name')
def objectify(self, form, data):
employee = super(EmployeesView, self).objectify(form, data)
self.update_stores(employee, data)
self.update_departments(employee, data)
return employee
def update_stores(self, employee, data):
old_stores = set([s.uuid for s in employee.stores])
new_stores = data['stores']
for uuid in new_stores:
if uuid not in old_stores:
employee._stores.append(model.EmployeeStore(store_uuid=uuid))
for uuid in old_stores:
if uuid not in new_stores:
store = self.Session.query(model.Store).get(uuid)
employee.stores.remove(store)
def update_departments(self, employee, data):
old_depts = set([d.uuid for d in employee.departments])
new_depts = data['departments']
for uuid in new_depts:
if uuid not in old_depts:
employee._departments.append(model.EmployeeDepartment(department_uuid=uuid))
for uuid in old_depts:
if uuid not in new_depts:
dept = self.Session.query(model.Department).get(uuid)
employee.departments.remove(dept)
def get_possible_stores(self):
return self.Session.query(model.Store)\
.order_by(model.Store.name)
def get_possible_departments(self):
return self.Session.query(model.Department)\
.order_by(model.Department.name)
def render_person(self, employee, field):
person = employee.person if employee else None
if not person:
return ""
text = six.text_type(person)
url = self.request.route_url('people.view', uuid=person.uuid)
return tags.link_to(text, url)
def render_stores(self, employee, field):
stores = employee.stores if employee else None
if not stores:
return ""
items = HTML.literal('')
for store in sorted(stores, key=six.text_type):
items += HTML.tag('li', c=six.text_type(store))
return HTML.tag('ul', c=items)
def render_departments(self, employee, field):
departments = employee.departments if employee else None
if not departments:
return ""
items = HTML.literal('')
for department in sorted(departments, key=six.text_type):
items += HTML.tag('li', c=six.text_type(department))
return HTML.tag('ul', c=items)
def get_version_child_classes(self):
return [
@ -167,62 +263,6 @@ class EmployeesView(MasterView):
]
class StoresField(fa.Field):
def __init__(self, name, **kwargs):
kwargs.setdefault('type', fa.types.Set)
kwargs.setdefault('options', Session.query(model.Store).order_by(model.Store.name))
kwargs.setdefault('value', self.get_value)
kwargs.setdefault('multiple', True)
kwargs.setdefault('size', 3)
fa.Field.__init__(self, name=name, **kwargs)
def get_value(self, employee):
return [s.uuid for s in employee.stores]
def sync(self):
if not self.is_readonly():
employee = self.parent.model
old_stores = set([s.uuid for s in employee.stores])
new_stores = set(self._deserialize())
for uuid in new_stores:
if uuid not in old_stores:
employee._stores.append(model.EmployeeStore(store_uuid=uuid))
for uuid in old_stores:
if uuid not in new_stores:
store = Session.query(model.Store).get(uuid)
assert store
employee.stores.remove(store)
class DepartmentsField(fa.Field):
def __init__(self, name, **kwargs):
kwargs.setdefault('type', fa.types.Set)
kwargs.setdefault('options', Session.query(model.Department).order_by(model.Department.name))
kwargs.setdefault('value', self.get_value)
kwargs.setdefault('multiple', True)
kwargs.setdefault('size', 10)
fa.Field.__init__(self, name=name, **kwargs)
def get_value(self, employee):
return [d.uuid for d in employee.departments]
def sync(self):
if not self.is_readonly():
employee = self.parent.model
old_depts = set([d.uuid for d in employee.departments])
new_depts = set(self._deserialize())
for uuid in new_depts:
if uuid not in old_depts:
employee._departments.append(model.EmployeeDepartment(department_uuid=uuid))
for uuid in old_depts:
if uuid not in new_depts:
dept = Session.query(model.Department).get(uuid)
assert dept
employee.departments.remove(dept)
class EmployeesAutocomplete(AutocompleteView):
"""
Autocomplete view for the Employee model, but restricted to return only

View file

@ -250,8 +250,6 @@ class MasterView(View):
# let save_create_form() return alternate object if necessary
obj = self.save_create_form(form) or form.fieldset.model
self.after_create(obj)
# TODO: ugh, avoiding refactor for now but it's needed
self.after_create_form(form, obj)
self.flash_after_create(obj)
return self.redirect_after_create(obj)
context = {'form': form}
@ -1625,11 +1623,6 @@ class MasterView(View):
Event hook, called just after a new instance is saved.
"""
def after_create_form(self, form, obj):
"""
Event hook, called just after a new instance is saved.
"""
def editable_instance(self, instance):
"""
Returns boolean indicating whether or not the given instance can be
@ -1643,11 +1636,6 @@ class MasterView(View):
Event hook, called just after an existing instance is saved.
"""
def after_edit_form(self, form, obj):
"""
Event hook, called just after an instance is updated.
"""
def deletable_instance(self, instance):
"""
Returns boolean indicating whether or not the given instance can be

View file

@ -152,9 +152,13 @@ class MasterView3(MasterView2):
return False
return True
def objectify(self, form, data):
obj = form.schema.objectify(data, context=form.model_instance)
return obj
def save_create_form(self, form):
self.before_create(form)
obj = form.schema.objectify(self.form_deserialized)
obj = self.objectify(form, self.form_deserialized)
self.before_create_flush(obj, form)
self.Session.add(obj)
self.Session.flush()
@ -164,8 +168,6 @@ class MasterView3(MasterView2):
pass
def save_edit_form(self, form):
obj = form.schema.objectify(self.form_deserialized, context=form.model_instance)
obj = self.objectify(form, self.form_deserialized)
self.after_edit(obj)
# TODO: ugh, this is to avoid refactor for the moment..but it's needed..
self.after_edit_form(form, obj)
self.Session.flush()