diff --git a/tailbone/forms/__init__.py b/tailbone/forms/__init__.py index ae08b390..ffdcef13 100644 --- a/tailbone/forms/__init__.py +++ b/tailbone/forms/__init__.py @@ -29,7 +29,7 @@ from formencode import Schema from .core import Form, Field, FieldSet from .simpleform import SimpleForm, FormRenderer from .alchemy import AlchemyForm -from .fields import * +from .fields import AssociationProxyField from .renderers import * from tailbone.forms import renderers diff --git a/tailbone/forms/fields.py b/tailbone/forms/fields.py index a5ad17f5..ebeda61f 100644 --- a/tailbone/forms/fields.py +++ b/tailbone/forms/fields.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2012 Lance Edgar +# Copyright © 2010-2015 Lance Edgar # # This file is part of Rattail. # @@ -21,17 +20,15 @@ # along with Rattail. If not, see . # ################################################################################ +""" +FormAlchemy Fields +""" -""" -``tailbone.forms.fields`` -- FormAlchemy Fields -""" +from __future__ import unicode_literals, absolute_import from formalchemy import Field -__all__ = ['AssociationProxyField'] - - def AssociationProxyField(name, **kwargs): """ Returns a FormAlchemy ``Field`` class which is aware of association diff --git a/tailbone/templates/people/crud.mako b/tailbone/templates/people/crud.mako deleted file mode 100644 index 6f1f77fa..00000000 --- a/tailbone/templates/people/crud.mako +++ /dev/null @@ -1,13 +0,0 @@ -## -*- coding: utf-8 -*- -<%inherit file="/crud.mako" /> - -<%def name="context_menu_items()"> -
  • ${h.link_to("Back to People", url('people'))}
  • - % if form.readonly: -
  • ${h.link_to("Edit this Person", url('person.update', uuid=form.fieldset.model.uuid))}
  • - % elif form.updating: -
  • ${h.link_to("View this Person", url('person.read', uuid=form.fieldset.model.uuid))}
  • - % endif - - -${parent.body()} diff --git a/tailbone/templates/people/index.mako b/tailbone/templates/people/index.mako deleted file mode 100644 index 9e1ca615..00000000 --- a/tailbone/templates/people/index.mako +++ /dev/null @@ -1,12 +0,0 @@ -## -*- coding: utf-8 -*- -<%inherit file="/grid.mako" /> - -<%def name="title()">People - -<%def name="context_menu_items()"> -## % if request.has_perm('people.create'): -##
  • ${h.link_to("Create a new Person", url('person.new'))}
  • -## % endif - - -${parent.body()} diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 0889c87c..78b9fd21 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2012 Lance Edgar +# Copyright © 2010-2015 Lance Edgar # # This file is part of Rattail. # @@ -21,63 +20,53 @@ # along with Rattail. If not, see . # ################################################################################ - """ Person Views """ -from sqlalchemy import and_ +from __future__ import unicode_literals, absolute_import -from . import SearchableAlchemyGridView, CrudView, AutocompleteView +import sqlalchemy as sa -from ..db import Session +from pyramid.httpexceptions import HTTPFound, HTTPNotFound + +from tailbone.views import MasterView, AutocompleteView from rattail.db import model -from rattail.db.model import (Person, PersonEmailAddress, PersonPhoneNumber, - VendorContact) -class PeopleGrid(SearchableAlchemyGridView): +class PeopleView(MasterView): + """ + Master view for the Person class. + """ + model_class = model.Person + model_title_plural = "People" + route_prefix = 'people' - mapped_class = Person - config_prefix = 'people' - sort = 'display_name' + def configure_grid(self, g): + g.joiners['email'] = lambda q: q.outerjoin(model.PersonEmailAddress, sa.and_( + model.PersonEmailAddress.parent_uuid == model.Person.uuid, + model.PersonEmailAddress.preference == 1)) + g.joiners['phone'] = lambda q: q.outerjoin(model.PersonPhoneNumber, sa.and_( + model.PersonPhoneNumber.parent_uuid == model.Person.uuid, + model.PersonPhoneNumber.preference == 1)) - def join_map(self): - return { - 'email': - lambda q: q.outerjoin(PersonEmailAddress, and_( - PersonEmailAddress.parent_uuid == Person.uuid, - PersonEmailAddress.preference == 1)), - 'phone': - lambda q: q.outerjoin(PersonPhoneNumber, and_( - PersonPhoneNumber.parent_uuid == Person.uuid, - PersonPhoneNumber.preference == 1)), - } + g.filters['email'] = g.make_filter('email', model.PersonEmailAddress.address, + label="Email Address") + g.filters['phone'] = g.make_filter('phone', model.PersonPhoneNumber.number, + label="Phone Number") - def filter_map(self): - return self.make_filter_map( - ilike=['first_name', 'last_name', 'display_name'], - email=self.filter_ilike(PersonEmailAddress.address), - phone=self.filter_ilike(PersonPhoneNumber.number)) + g.filters['first_name'].default_active = True + g.filters['first_name'].default_verb = 'contains' - def filter_config(self): - return self.make_filter_config( - include_filter_first_name=True, - filter_type_first_name='lk', - include_filter_last_name=True, - filter_type_last_name='lk', - filter_label_phone="Phone Number", - filter_label_email="Email Address") + g.filters['last_name'].default_active = True + g.filters['last_name'].default_verb = 'contains' - def sort_map(self): - return self.make_sort_map( - 'first_name', 'last_name', 'display_name', - email=self.sorter(PersonEmailAddress.address), - phone=self.sorter(PersonPhoneNumber.number)) + g.sorters['email'] = lambda q, d: q.order_by(getattr(model.PersonEmailAddress.address, d)()) + g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.PersonPhoneNumber.number, d)()) + + g.default_sortkey = 'display_name' - def grid(self): - g = self.make_grid() g.configure( include=[ g.first_name, @@ -85,38 +74,22 @@ class PeopleGrid(SearchableAlchemyGridView): g.display_name, g.phone.label("Phone Number"), g.email.label("Email Address"), - ], + ], readonly=True) - if self.request.has_perm('people.read'): - g.viewable = True - g.view_route_name = 'person.read' - if self.request.has_perm('people.update'): - g.editable = True - g.edit_route_name = 'person.update' - # if self.request.has_perm('products.delete'): - # g.deletable = True - # g.delete_route_name = 'product.delete' + def get_instance(self): + # TODO: I don't recall why this fallback check for a vendor contact + # exists here, but leaving it intact for now. + key = self.request.matchdict['uuid'] + instance = self.Session.query(model.Person).get(key) + if instance: + return instance + instance = self.Session.query(model.VendorContact).get(key) + if instance: + return instance.person + raise HTTPNotFound - return g - - -class PersonCrud(CrudView): - - mapped_class = Person - home_route = 'people' - - def get_model(self, key): - model = super(PersonCrud, self).get_model(key) - if model: - return model - model = Session.query(VendorContact).get(key) - if model: - return model.person - return None - - def fieldset(self, model): - fs = self.make_fieldset(model) + def configure_fieldset(self, fs): fs.configure( include=[ fs.first_name, @@ -124,13 +97,12 @@ class PersonCrud(CrudView): fs.display_name, fs.phone.label("Phone Number").readonly(), fs.email.label("Email Address").readonly(), - ]) - return fs + ]) class PeopleAutocomplete(AutocompleteView): - mapped_class = Person + mapped_class = model.Person fieldname = 'display_name' @@ -144,34 +116,13 @@ class PeopleEmployeesAutocomplete(PeopleAutocomplete): return q.join(model.Employee) -def add_routes(config): - config.add_route('people', '/people') - config.add_route('people.autocomplete', '/people/autocomplete') - config.add_route(u'people.autocomplete.employees', u'/people/autocomplete/employees') - config.add_route('person.read', '/people/{uuid}') - config.add_route('person.update', '/people/{uuid}/edit') - - def includeme(config): - add_routes(config) + PeopleView.defaults(config) - # List - config.add_view(PeopleGrid, route_name='people', - renderer='/people/index.mako', - permission='people.list') - - # CRUD - config.add_view(PersonCrud, attr='read', route_name='person.read', - renderer='/people/crud.mako', - permission='people.read') - config.add_view(PersonCrud, attr='update', route_name='person.update', - renderer='/people/crud.mako', - permission='people.update') - - # Autocomplete + # autocomplete + config.add_route('people.autocomplete', '/people/autocomplete') config.add_view(PeopleAutocomplete, route_name='people.autocomplete', - renderer='json', - permission='people.list') - config.add_view(PeopleEmployeesAutocomplete, route_name=u'people.autocomplete.employees', - renderer=u'json', - permission=u'people.list') + renderer='json', permission='people.list') + config.add_route('people.autocomplete.employees', '/people/autocomplete/employees') + config.add_view(PeopleEmployeesAutocomplete, route_name='people.autocomplete.employees', + renderer='json', permission='people.list')