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
-%def>
-
-${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>
-
-<%def name="context_menu_items()">
-## % if request.has_perm('people.create'):
-## ${h.link_to("Create a new Person", url('person.new'))}
-## % endif
-%def>
-
-${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')