diff --git a/docs/api/views/batch.rst b/docs/api/views/batch.rst
index a98fc39d..4de15486 100644
--- a/docs/api/views/batch.rst
+++ b/docs/api/views/batch.rst
@@ -5,24 +5,12 @@
.. automodule:: tailbone.views.batch
-.. autoclass:: BatchGrid
- :members:
-
-.. autoclass:: FileBatchGrid
- :members:
-
.. autoclass:: BatchCrud
:members:
.. autoclass:: FileBatchCrud
:members:
-.. autoclass:: BatchRowGrid
- :members:
-
-.. autoclass:: ProductBatchRowGrid
- :members:
-
.. autoclass:: BatchRowCrud
:members:
diff --git a/tailbone/grids/__init__.py b/tailbone/grids/__init__.py
deleted file mode 100644
index 030c0da7..00000000
--- a/tailbone/grids/__init__.py
+++ /dev/null
@@ -1,32 +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 .
-#
-################################################################################
-"""
-Grids
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from .core import *
-from .alchemy import AlchemyGrid
-from . import util
-from . import search
diff --git a/tailbone/grids/alchemy.py b/tailbone/grids/alchemy.py
deleted file mode 100644
index c7e13bbb..00000000
--- a/tailbone/grids/alchemy.py
+++ /dev/null
@@ -1,123 +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 .
-#
-################################################################################
-"""
-FormAlchemy Grid Classes
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from sqlalchemy.orm import object_session
-
-try:
- from sqlalchemy.inspection import inspect
-except ImportError:
- inspect = None
- from sqlalchemy.orm import class_mapper
-
-from rattail.util import prettify
-
-import formalchemy as fa
-from webhelpers.html import tags
-from webhelpers.html import HTML
-
-from tailbone.db import Session
-from tailbone.grids.core import Grid
-
-
-class AlchemyGrid(Grid):
-
- sort_map = {}
-
- pager = None
- pager_format = '$link_first $link_previous ~1~ $link_next $link_last'
-
- def __init__(self, request, cls, instances, **kwargs):
- super(AlchemyGrid, self).__init__(request, **kwargs)
- self._formalchemy_grid = fa.Grid(cls, instances, session=Session(),
- request=request)
- self._formalchemy_grid.prettify = prettify
-
- def __delattr__(self, attr):
- delattr(self._formalchemy_grid, attr)
-
- def __getattr__(self, attr):
- return getattr(self._formalchemy_grid, attr)
-
- def cell_class(self, field):
- classes = [field.name]
- return ' '.join(classes)
-
- def checkbox(self, row):
- return tags.checkbox('check-'+row.uuid)
-
- def column_header(self, field):
- class_ = None
- label = field.label()
- if field.key in self.sort_map:
- class_ = 'sortable'
- if field.key == self.config['sort']:
- class_ += ' sorted ' + self.config['dir']
- label = tags.link_to(label, '#')
- return HTML.tag('th', class_=class_, field=field.key,
- title=self.column_titles.get(field.key), c=label)
-
- def crud_route_kwargs(self, row):
- if inspect:
- mapper = inspect(row.__class__)
- else:
- mapper = class_mapper(row.__class__)
- keys = [k.key for k in mapper.primary_key]
- values = [getattr(row, k) for k in keys]
- return dict(zip(keys, values))
-
- view_route_kwargs = crud_route_kwargs
- edit_route_kwargs = crud_route_kwargs
- delete_route_kwargs = crud_route_kwargs
-
- def iter_fields(self):
- return self._formalchemy_grid.render_fields.itervalues()
-
- def iter_rows(self):
- for row in self._formalchemy_grid.rows:
- self._formalchemy_grid._set_active(row, object_session(row))
- yield row
-
- def page_count_options(self):
- return [5, 10, 20, 50, 100]
-
- def page_links(self):
- return self.pager.pager(self.pager_format,
- symbol_next='next',
- symbol_previous='prev',
- onclick="grid_navigate_page(this, '$partial_url'); return false;")
-
- def render_field(self, field):
- if self._formalchemy_grid.readonly:
- return field.render_readonly()
- return field.render()
-
- def row_attrs(self, row, i):
- attrs = super(AlchemyGrid, self).row_attrs(row, i)
- if hasattr(row, 'uuid'):
- attrs['uuid'] = row.uuid
- return attrs
diff --git a/tailbone/grids/core.py b/tailbone/grids/core.py
deleted file mode 100644
index 911441ae..00000000
--- a/tailbone/grids/core.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2012 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 .
-#
-################################################################################
-
-"""
-Core Grid Classes
-"""
-
-try:
- from collections import OrderedDict
-except ImportError:
- from ordereddict import OrderedDict
-
-from webhelpers.html import HTML
-from webhelpers.html.builder import format_attrs
-
-from pyramid.renderers import render
-
-from rattail.core import Object
-
-
-__all__ = ['Grid']
-
-
-class Grid(Object):
-
- full = False
- hoverable = True
- checkboxes = False
- partial_only = False
-
- viewable = False
- view_route_name = None
- view_route_kwargs = None
-
- editable = False
- edit_route_name = None
- edit_route_kwargs = None
-
- deletable = False
- delete_route_name = None
- delete_route_kwargs = None
-
- # Set this to a callable to allow ad-hoc row class additions.
- extra_row_class = None
-
- def __init__(self, request, **kwargs):
- kwargs.setdefault('fields', OrderedDict())
- kwargs.setdefault('column_titles', {})
- kwargs.setdefault('extra_columns', [])
- super(Grid, self).__init__(**kwargs)
- self.request = request
-
- def add_column(self, name, label, callback):
- self.extra_columns.append(
- Object(name=name, label=label, callback=callback))
-
- def column_header(self, field):
- return HTML.tag('th', field=field.name,
- title=self.column_titles.get(field.name),
- c=field.label)
-
- def div_attrs(self):
- classes = ['grid']
- if self.full:
- classes.append('full')
- if self.hoverable:
- classes.append('hoverable')
- return format_attrs(
- class_=' '.join(classes),
- url=self.request.current_route_url(_query=None))
-
- def get_view_url(self, row):
- kwargs = {}
- if self.view_route_kwargs:
- if callable(self.view_route_kwargs):
- kwargs = self.view_route_kwargs(row)
- else:
- kwargs = self.view_route_kwargs
- return self.request.route_url(self.view_route_name, **kwargs)
-
- def get_edit_url(self, row):
- kwargs = {}
- if self.edit_route_kwargs:
- if callable(self.edit_route_kwargs):
- kwargs = self.edit_route_kwargs(row)
- else:
- kwargs = self.edit_route_kwargs
- return self.request.route_url(self.edit_route_name, **kwargs)
-
- def get_delete_url(self, row):
- kwargs = {}
- if self.delete_route_kwargs:
- if callable(self.delete_route_kwargs):
- kwargs = self.delete_route_kwargs(row)
- else:
- kwargs = self.delete_route_kwargs
- return self.request.route_url(self.delete_route_name, **kwargs)
-
- def get_row_attrs(self, row, i):
- attrs = self.row_attrs(row, i)
- return format_attrs(**attrs)
-
- def row_attrs(self, row, i):
- return {'class_': self.get_row_class(row, i)}
-
- def get_row_class(self, row, i):
- class_ = self.default_row_class(row, i)
- if callable(self.extra_row_class):
- extra = self.extra_row_class(row, i)
- if extra:
- class_ = '{0} {1}'.format(class_, extra)
- return class_
-
- def default_row_class(self, row, i):
- return 'odd' if i % 2 else 'even'
-
- def iter_fields(self):
- return self.fields.itervalues()
-
- def iter_rows(self):
- """
- Iterate over the grid rows. The default implementation simply returns
- an iterator over ``self.rows``; note however that by default there is
- no such attribute. You must either populate that, or overrirde this
- method.
- """
- return iter(self.rows)
-
- def render(self, template='/grids/grid.mako', **kwargs):
- kwargs.setdefault('grid', self)
- return render(template, kwargs)
-
- def render_field(self, field):
- raise NotImplementedError
diff --git a/tailbone/grids/search.py b/tailbone/grids/search.py
deleted file mode 100644
index 2bc2861f..00000000
--- a/tailbone/grids/search.py
+++ /dev/null
@@ -1,382 +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 .
-#
-################################################################################
-"""
-Grid Search Filters
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import re
-
-from sqlalchemy import func, or_
-
-from webhelpers.html import tags
-from webhelpers.html import literal
-
-from pyramid.renderers import render
-from pyramid_simpleform import Form
-from pyramid_simpleform.renderers import FormRenderer
-
-from rattail.core import Object
-from rattail.gpc import GPC
-from rattail.util import prettify
-
-
-class SearchFilter(Object):
- """
- Base class and default implementation for search filters.
- """
-
- def __init__(self, name, label=None, **kwargs):
- Object.__init__(self, **kwargs)
- self.name = name
- self.label = label or prettify(name)
-
- def types_select(self):
- types = [
- ('is', "is"),
- ('nt', "is not"),
- ('lk', "contains"),
- ('nl', "doesn't contain"),
- (u'sx', u"sounds like"),
- (u'nx', u"doesn't sound like"),
- ]
- options = []
- filter_map = self.search.filter_map[self.name]
- for value, label in types:
- if value in filter_map:
- options.append((value, label))
- return tags.select('filter_type_'+self.name,
- self.search.config.get('filter_type_'+self.name),
- options, class_='filter-type')
-
- def value_control(self):
- return tags.text(self.name, self.search.config.get(self.name))
-
-
-class BooleanSearchFilter(SearchFilter):
- """
- Boolean search filter.
- """
-
- def value_control(self):
- return tags.select(self.name, self.search.config.get(self.name),
- ["True", "False"])
-
-
-class ChoiceSearchFilter(SearchFilter):
- """
- Generic search filter where the user may only select among a specific set
- of choices.
- """
-
- def __init__(self, choices):
- self.choices = choices
-
- def __call__(self, name, label=None, **kwargs):
- super(ChoiceSearchFilter, self).__init__(name, label=label, **kwargs)
- return self
-
- def value_control(self):
- return tags.select(self.name, self.search.config.get(self.name), self.choices)
-
-
-def EnumSearchFilter(enum):
- options = enum.items()
-
- class EnumSearchFilter(SearchFilter):
- def value_control(self):
- return tags.select(self.name, self.search.config.get(self.name), options)
-
- return EnumSearchFilter
-
-
-class SearchForm(Form):
- """
- Generic form class which aggregates :class:`SearchFilter` instances.
- """
-
- def __init__(self, request, filter_map, config, *args, **kwargs):
- super(SearchForm, self).__init__(request, *args, **kwargs)
- self.filter_map = filter_map
- self.config = config
- self.filters = {}
-
- def add_filter(self, filter_):
- filter_.search = self
- self.filters[filter_.name] = filter_
-
-
-class SearchFormRenderer(FormRenderer):
- """
- Renderer for :class:`SearchForm` instances.
- """
-
- def __init__(self, form, *args, **kwargs):
- super(SearchFormRenderer, self).__init__(form, *args, **kwargs)
- self.request = form.request
- self.filters = form.filters
- self.config = form.config
-
- def add_filter(self, visible):
- options = ['add a filter']
- for f in self.sorted_filters():
- options.append((f.name, f.label))
- return self.select('add-filter', options,
- style='display: none;' if len(visible) == len(self.filters) else None)
-
- def checkbox(self, name, checked=None, **kwargs):
- if name.startswith('include_filter_'):
- if checked is None:
- checked = self.config[name]
- return tags.checkbox(name, checked=checked, **kwargs)
- if checked is None:
- checked = False
- return super(SearchFormRenderer, self).checkbox(name, checked=checked, **kwargs)
-
- def render(self, **kwargs):
- kwargs['search'] = self
- return literal(render('/grids/search.mako', kwargs))
-
- def sorted_filters(self):
- return sorted(self.filters.values(), key=lambda x: x.label)
-
- def text(self, name, **kwargs):
- return tags.text(name, value=self.config.get(name), **kwargs)
-
-
-def filter_exact(field):
- """
- Convenience function which returns a filter map entry, with typical logic
- built in for "exact match" queries applied to ``field``.
- """
-
- return {
- 'is':
- lambda q, v: q.filter(field == v) if v else q,
- 'nt':
- lambda q, v: q.filter(field != v) if v else q,
- }
-
-
-def filter_ilike(field):
- """
- Convenience function which returns a filter map entry, with typical logic
- built in for "ILIKE" queries applied to ``field``.
- """
-
- def ilike(query, value):
- if value:
- query = query.filter(field.ilike('%%%s%%' % value))
- return query
-
- def not_ilike(query, value):
- if value:
- query = query.filter(or_(
- field == None,
- ~field.ilike('%%%s%%' % value),
- ))
- return query
-
- return {'lk': ilike, 'nl': not_ilike}
-
-
-def filter_int(field):
- """
- Returns a filter map entry for an integer field. This provides exact
- matching but also strips out non-numeric characters to avoid type errors.
- """
-
- def filter_is(q, v):
- v = re.sub(r'\D', '', v or '')
- return q.filter(field == int(v)) if v else q
-
- def filter_nt(q, v):
- v = re.sub(r'\D', '', v or '')
- return q.filter(field != int(v)) if v else q
-
- return {'is': filter_is, 'nt': filter_nt}
-
-
-def filter_soundex(field):
- """
- Returns a filter map entry which leverages the `soundex()` SQL function.
- """
-
- def soundex(query, value):
- if value:
- query = query.filter(func.soundex(field) == func.soundex(value))
- return query
-
- def not_soundex(query, value):
- if value:
- query = query.filter(func.soundex(field) != func.soundex(value))
- return query
-
- return {u'sx': soundex, u'nx': not_soundex}
-
-
-def filter_ilike_and_soundex(field):
- """
- Returns a filter map which provides both the `ilike` and `soundex`
- features.
- """
- filters = filter_ilike(field)
- filters.update(filter_soundex(field))
- return filters
-
-
-def filter_gpc(field):
- """
- Returns a filter suitable for a GPC field.
- """
-
- def filter_is(q, v):
- if not v:
- return q
- try:
- return q.filter(field.in_((
- GPC(v), GPC(v, calc_check_digit='upc'))))
- except ValueError:
- return q
-
- def filter_not(q, v):
- if not v:
- return q
- try:
- return q.filter(~field.in_((
- GPC(v), GPC(v, calc_check_digit='upc'))))
- except ValueError:
- return q
-
- return {'is': filter_is, 'nt': filter_not}
-
-
-def get_filter_config(prefix, request, filter_map, **kwargs):
- """
- Returns a configuration dictionary for a search form.
- """
-
- config = {}
-
- def update_config(dict_, prefix='', exclude_by_default=False):
- """
- Updates the ``config`` dictionary based on the contents of ``dict_``.
- """
-
- for field in filter_map:
- if prefix+'include_filter_'+field in dict_:
- include = dict_[prefix+'include_filter_'+field]
- include = bool(include) and include != '0'
- config['include_filter_'+field] = include
- elif exclude_by_default:
- config['include_filter_'+field] = False
- if prefix+'filter_type_'+field in dict_:
- config['filter_type_'+field] = dict_[prefix+'filter_type_'+field]
- if prefix+field in dict_:
- config[field] = dict_[prefix+field]
-
- # Update config to exclude all filters by default.
- for field in filter_map:
- config['include_filter_'+field] = False
-
- # Update config to honor default settings.
- config.update(kwargs)
-
- # Update config with data cached in session.
- update_config(request.session, prefix=prefix+'.')
-
- # Update config with data from GET/POST request.
- if request.params.get('filters') == 'true':
- update_config(request.params, exclude_by_default=True)
-
- # Cache filter data in session.
- for key in config:
- if (not key.startswith('filter_factory_')
- and not key.startswith('filter_label_')):
- request.session[prefix+'.'+key] = config[key]
-
- return config
-
-
-def get_filter_map(cls, exact=[], ilike=[], int_=[], **kwargs):
- """
- Convenience function which returns a "filter map" for ``cls``.
-
- ``exact``, if provided, should be a list of field names for which "exact"
- filtering is to be allowed.
-
- ``ilike``, if provided, should be a list of field names for which "ILIKE"
- filtering is to be allowed.
-
- ``int_``, if provided, should be a list of field names for which "integer"
- filtering is to be allowed.
-
- Any remaining ``kwargs`` are assumed to be filter map entries themselves,
- and are added directly to the map.
- """
-
- fmap = {}
- for name in exact:
- fmap[name] = filter_exact(getattr(cls, name))
- for name in ilike:
- fmap[name] = filter_ilike(getattr(cls, name))
- for name in int_:
- fmap[name] = filter_int(getattr(cls, name))
- fmap.update(kwargs)
- return fmap
-
-
-def get_search_form(request, filter_map, config):
- """
- Returns a :class:`SearchForm` instance with a :class:`SearchFilter` for
- each filter in ``filter_map``, using configuration from ``config``.
- """
-
- search = SearchForm(request, filter_map, config)
- for field in filter_map:
- factory = config.get('filter_factory_%s' % field, SearchFilter)
- label = config.get('filter_label_%s' % field)
- search.add_filter(factory(field, label=label))
- return search
-
-
-def filter_query(query, config, filter_map, join_map):
- """
- Filters the given query according to filter and sorting hints found within
- the config dictionary, using the filter and join maps as needed. The
- filtered query is returned.
- """
- joins = config.setdefault('joins', [])
- for key in config:
- if key.startswith('include_filter_') and config[key]:
- field = key[15:]
- value = config.get(field)
- if value != '':
- if field in join_map and field not in joins:
- query = join_map[field](query)
- joins.append(field)
- fmap = filter_map[field]
- filt = fmap[config['filter_type_'+field]]
- query = filt(query, value)
- return query
diff --git a/tailbone/grids/util.py b/tailbone/grids/util.py
deleted file mode 100644
index 6ecfd3f8..00000000
--- a/tailbone/grids/util.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2012 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 .
-#
-################################################################################
-
-"""
-Grid Utilities
-"""
-
-from sqlalchemy.orm.attributes import InstrumentedAttribute
-
-from webhelpers.html import literal
-
-from pyramid.response import Response
-
-from .search import SearchFormRenderer
-
-
-def get_sort_config(name, request, **kwargs):
- """
- Returns a configuration dictionary for grid sorting.
- """
-
- # Initial config uses some default values.
- config = {
- 'dir': 'asc',
- 'per_page': 20,
- 'page': 1,
- }
-
- # Override with defaults provided by caller.
- config.update(kwargs)
-
- # Override with values from GET/POST request and/or session.
- for key in config:
- full_key = name+'_'+key
- if request.params.get(key):
- value = request.params[key]
- config[key] = value
- request.session[full_key] = value
- elif request.session.get(full_key):
- value = request.session[full_key]
- config[key] = value
-
- return config
-
-
-def get_sort_map(cls, names=None, **kwargs):
- """
- Convenience function which returns a sort map for ``cls``.
-
- If ``names`` is not specified, the map will include all "standard" fields
- present on the mapped class. Otherwise, the map will be limited to only
- the fields which are named.
-
- All remaining ``kwargs`` are assumed to be sort map entries, and will be
- added to the map directly.
- """
-
- smap = {}
- if names is None:
- names = []
- for attr in cls.__dict__:
- obj = getattr(cls, attr)
- if isinstance(obj, InstrumentedAttribute):
- if obj.key != 'uuid':
- names.append(obj.key)
- for name in names:
- smap[name] = sorter(getattr(cls, name))
- smap.update(kwargs)
- return smap
-
-
-def render_grid(grid, search_form=None, **kwargs):
- """
- Convenience function to render ``grid`` (which should be a
- :class:`tailbone.grids.Grid` instance).
-
- This "usually" will return a dictionary to be used as context for rendering
- the final view template.
-
- However, if a partial grid is requested (or mandated), then the grid body
- will be rendered and a :class:`pyramid.response.Response` object will be
- returned instead.
- """
-
- if grid.partial_only or grid.request.params.get('partial'):
- return Response(body=grid.render(), content_type='text/html')
- kwargs['grid'] = literal(grid.render())
- if search_form:
- kwargs['search'] = SearchFormRenderer(search_form)
- return kwargs
-
-
-def sort_query(query, config, sort_map, join_map={}):
- """
- Sorts ``query`` according to ``config`` and ``sort_map``. ``join_map`` is
- used, if necessary, to join additional tables to the base query. The
- sorted query is returned.
- """
-
- field = config.get('sort')
- if not field:
- return query
- joins = config.setdefault('joins', [])
- if field in join_map and field not in joins:
- query = join_map[field](query)
- joins.append(field)
- sort = sort_map[field]
- return sort(query, config['dir'])
-
-
-def sorter(field):
- """
- Returns a function suitable for a sort map callable, with typical logic
- built in for sorting applied to ``field``.
- """
-
- return lambda q, d: q.order_by(getattr(field, d)())
diff --git a/tailbone/views/__init__.py b/tailbone/views/__init__.py
index aa07933f..5719965f 100644
--- a/tailbone/views/__init__.py
+++ b/tailbone/views/__init__.py
@@ -32,9 +32,6 @@ from .master import MasterView
# TODO: deprecate / remove some of this
from .autocomplete import AutocompleteView
from .crud import CrudView
-from .grids import (
- GridView, AlchemyGridView, SortableAlchemyGridView,
- PagedAlchemyGridView, SearchableAlchemyGridView)
def includeme(config):
diff --git a/tailbone/views/batch/__init__.py b/tailbone/views/batch/__init__.py
index e9d5b708..000d58d1 100644
--- a/tailbone/views/batch/__init__.py
+++ b/tailbone/views/batch/__init__.py
@@ -1,8 +1,8 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2016 Lance Edgar
+# Copyright © 2010-2017 Lance Edgar
#
# This file is part of Rattail.
#
@@ -29,5 +29,5 @@ from __future__ import unicode_literals, absolute_import
from .core import BatchMasterView, FileBatchMasterView
# TODO: deprecate / remove this
-from .core import (BaseGrid, BatchGrid, FileBatchGrid, BaseCrud, BatchCrud, FileBatchCrud,
- StatusRenderer, BatchRowGrid, ProductBatchRowGrid, BatchRowCrud, defaults)
+from .core import (BaseCrud, BatchCrud, FileBatchCrud,
+ StatusRenderer, BatchRowCrud, defaults)
diff --git a/tailbone/views/batch/core.py b/tailbone/views/batch/core.py
index 05cc9f22..44bb387c 100644
--- a/tailbone/views/batch/core.py
+++ b/tailbone/views/batch/core.py
@@ -51,9 +51,8 @@ from webhelpers.html import HTML, tags
from tailbone import forms, newgrids as grids
from tailbone.db import Session
-from tailbone.views import MasterView, SearchableAlchemyGridView, CrudView
+from tailbone.views import MasterView, CrudView
from tailbone.forms.renderers.batch import FileFieldRenderer
-from tailbone.grids.search import BooleanSearchFilter, EnumSearchFilter
from tailbone.progress import SessionProgress
@@ -1137,264 +1136,6 @@ class FileBatchMasterView(BatchMasterView):
"Download existing {} data file".format(model_title))
-class BaseGrid(SearchableAlchemyGridView):
- """
- Base view for batch and batch row grid views. You should not derive from
- this class, but :class:`BatchGrid` or :class:`BatchRowGrid` instead.
- """
-
- @property
- def config_prefix(self):
- """
- Config prefix for the grid view. This is used to keep track of current
- filtering and sorting, within the user's session. Derived classes may
- override this.
- """
- return self.mapped_class.__name__.lower()
-
- @property
- def permission_prefix(self):
- """
- Permission prefix for the grid view. This is used to automatically
- protect certain views common to all batches. Derived classes can
- override this.
- """
- return self.route_prefix
-
- def join_map_extras(self):
- """
- Derived classes can override this. The value returned will be used to
- supplement the default join map.
- """
- return {}
-
- def filter_map_extras(self):
- """
- Derived classes can override this. The value returned will be used to
- supplement the default filter map.
- """
- return {}
-
- def make_filter_map(self, **kwargs):
- """
- Make a filter map by combining kwargs from the base class, with extras
- supplied by a derived class.
- """
- extras = self.filter_map_extras()
- exact = extras.pop('exact', None)
- if exact:
- kwargs.setdefault('exact', []).extend(exact)
- ilike = extras.pop('ilike', None)
- if ilike:
- kwargs.setdefault('ilike', []).extend(ilike)
- kwargs.update(extras)
- return super(BaseGrid, self).make_filter_map(**kwargs)
-
- def filter_config_extras(self):
- """
- Derived classes can override this. The value returned will be used to
- supplement the default filter config.
- """
- return {}
-
- def sort_map_extras(self):
- """
- Derived classes can override this. The value returned will be used to
- supplement the default sort map.
- """
- return {}
-
- def _configure_grid(self, grid):
- """
- Internal method for configuring the grid. This is meant only for base
- classes; derived classes should not need to override it.
- """
-
- def configure_grid(self, grid):
- """
- Derived classes can override this. Customizes a grid which has already
- been created with defaults by the base class.
- """
-
-
-class BatchGrid(BaseGrid):
- """
- Base grid view for batches, which can be filtered and sorted.
- """
-
- @property
- def batch_class(self):
- raise NotImplementedError
-
- @property
- def mapped_class(self):
- return self.batch_class
-
- @property
- def batch_display(self):
- """
- Singular display text for the batch type, e.g. "Vendor Invoice".
- Override this as necessary.
- """
- return self.batch_class.__name__
-
- @property
- def batch_display_plural(self):
- """
- Plural display text for the batch type, e.g. "Vendor Invoices".
- Override this as necessary.
- """
- return "{0}s".format(self.batch_display)
-
- def join_map(self):
- """
- Provides the default join map for batch grid views. Derived classes
- should *not* override this, but :meth:`join_map_extras()` instead.
- """
- map_ = {
- 'created_by':
- lambda q: q.join(model.User, model.User.uuid == self.batch_class.created_by_uuid),
- 'executed_by':
- lambda q: q.outerjoin(model.User, model.User.uuid == self.batch_class.executed_by_uuid),
- }
- map_.update(self.join_map_extras())
- return map_
-
- def filter_map(self):
- """
- Provides the default filter map for batch grid views. Derived classes
- should *not* override this, but :meth:`filter_map_extras()` instead.
- """
-
- def executed_is(q, v):
- if v == 'True':
- return q.filter(self.batch_class.executed != None)
- else:
- return q.filter(self.batch_class.executed == None)
-
- def executed_nt(q, v):
- if v == 'True':
- return q.filter(self.batch_class.executed == None)
- else:
- return q.filter(self.batch_class.executed != None)
-
- return self.make_filter_map(
- executed={'is': executed_is, 'nt': executed_nt})
-
- def filter_config(self):
- """
- Provides the default filter config for batch grid views. Derived
- classes should *not* override this, but :meth:`filter_config_extras()`
- instead.
- """
- defaults = self.filter_config_extras()
- config = self.make_filter_config(
- filter_factory_executed=BooleanSearchFilter,
- filter_type_executed='is',
- executed=False,
- include_filter_executed=True)
- defaults.update(config)
- return defaults
-
- def sort_map(self):
- """
- Provides the default sort map for batch grid views. Derived classes
- should *not* override this, but :meth:`sort_map_extras()` instead.
- """
- map_ = self.make_sort_map(
- created_by=self.sorter(model.User.username),
- executed_by=self.sorter(model.User.username))
- map_.update(self.sort_map_extras())
- return map_
-
- def sort_config(self):
- """
- Provides the default sort config for batch grid views. Derived classes
- may override this.
- """
- return self.make_sort_config(sort='created', dir='desc')
-
- def grid(self):
- """
- Creates the grid for the view. Derived classes should *not* override
- this, but :meth:`configure_grid()` instead.
- """
- g = self.make_grid()
- g.created_by.set(renderer=forms.renderers.UserFieldRenderer)
- g.cognized_by.set(renderer=forms.renderers.UserFieldRenderer)
- g.executed_by.set(renderer=forms.renderers.UserFieldRenderer)
- self._configure_grid(g)
- self.configure_grid(g)
- if self.request.has_perm('{0}.view'.format(self.permission_prefix)):
- g.viewable = True
- g.view_route_name = '{0}.view'.format(self.route_prefix)
- if self.request.has_perm('{0}.edit'.format(self.permission_prefix)):
- g.editable = True
- g.edit_route_name = '{0}.edit'.format(self.route_prefix)
- if self.request.has_perm('{0}.delete'.format(self.permission_prefix)):
- g.deletable = True
- g.delete_route_name = '{0}.delete'.format(self.route_prefix)
- return g
-
- def _configure_grid(self, grid):
- grid.created_by.set(label="Created by")
- grid.executed_by.set(label="Executed by")
-
- def configure_grid(self, grid):
- """
- Derived classes can override this. Customizes a grid which has already
- been created with defaults by the base class.
- """
- g = grid
- g.configure(
- include=[
- g.created,
- g.created_by,
- g.executed,
- g.executed_by,
- ],
- readonly=True)
-
- def render_kwargs(self):
- """
- Add some things to the template context: batch type display name, route
- and permission prefixes.
- """
- return {
- 'batch_display': self.batch_display,
- 'batch_display_plural': self.batch_display_plural,
- 'route_prefix': self.route_prefix,
- 'permission_prefix': self.permission_prefix,
- }
-
-
-class FileBatchGrid(BatchGrid):
- """
- Base grid view for batches, which involve primarily a file upload.
- """
-
- def _configure_grid(self, g):
- super(FileBatchGrid, self)._configure_grid(g)
- g.created.set(label="Uploaded")
- g.created_by.set(label="Uploaded by")
-
- def configure_grid(self, grid):
- """
- Derived classes can override this. Customizes a grid which has already
- been created with defaults by the base class.
- """
- g = grid
- g.configure(
- include=[
- g.created,
- g.created_by,
- g.filename,
- g.executed,
- g.executed_by,
- ],
- readonly=True)
-
-
class BaseCrud(CrudView):
"""
Base CRUD view for batches and batch rows.
@@ -2000,173 +1741,6 @@ class StatusRenderer(forms.renderers.EnumFieldRenderer):
return status_code_text
-class BatchRowGrid(BaseGrid):
- """
- Base grid view for batch rows, which can be filtered and sorted. Also it
- can delete all rows matching the current list view query.
- """
-
- @property
- def row_class(self):
- raise NotImplementedError
-
- @property
- def mapped_class(self):
- return self.row_class
-
- @property
- def config_prefix(self):
- """
- Config prefix for the grid view. This is used to keep track of current
- filtering and sorting, within the user's session. Derived classes may
- override this.
- """
- return '{0}.{1}'.format(self.mapped_class.__name__.lower(),
- self.request.matchdict['uuid'])
-
- @property
- def batch_class(self):
- """
- Model class of the batch to which the rows belong.
- """
- return self.row_class.__batch_class__
-
- @property
- def batch_display(self):
- """
- Singular display text for the batch type, e.g. "Vendor Invoice".
- Override this as necessary.
- """
- return self.batch_class.__name__
-
- def current_batch(self):
- """
- Return the current batch, based on the UUID within the URL.
- """
- return Session.query(self.batch_class).get(self.request.matchdict['uuid'])
-
- def modify_query(self, q):
- q = super(BatchRowGrid, self).modify_query(q)
- q = q.filter(self.row_class.batch == self.current_batch())
- q = q.filter(self.row_class.removed == False)
- return q
-
- def join_map(self):
- """
- Provides the default join map for batch row grid views. Derived
- classes should *not* override this, but :meth:`join_map_extras()`
- instead.
- """
- return self.join_map_extras()
-
- def filter_map(self):
- """
- Provides the default filter map for batch row grid views. Derived
- classes should *not* override this, but :meth:`filter_map_extras()`
- instead.
- """
- return self.make_filter_map(exact=['status_code'])
-
- def filter_config(self):
- """
- Provides the default filter config for batch grid views. Derived
- classes should *not* override this, but :meth:`filter_config_extras()`
- instead.
- """
- kwargs = {'filter_label_status_code': "Status",
- 'filter_factory_status_code': EnumSearchFilter(self.row_class.STATUS)}
- kwargs.update(self.filter_config_extras())
- return self.make_filter_config(**kwargs)
-
- def sort_map(self):
- """
- Provides the default sort map for batch grid views. Derived classes
- should *not* override this, but :meth:`sort_map_extras()` instead.
- """
- map_ = self.make_sort_map()
- map_.update(self.sort_map_extras())
- return map_
-
- def sort_config(self):
- """
- Provides the default sort config for batch grid views. Derived classes
- may override this.
- """
- return self.make_sort_config(sort='sequence', dir='asc')
-
- def grid(self):
- """
- Creates the grid for the view. Derived classes should *not* override
- this, but :meth:`configure_grid()` instead.
- """
- g = self.make_grid()
- g.extra_row_class = self.tr_class
- g.sequence.set(label="Seq.")
- g.status_code.set(label="Status", renderer=StatusRenderer(self.row_class.STATUS))
- self._configure_grid(g)
- self.configure_grid(g)
-
- batch = self.current_batch()
- g.viewable = True
- g.view_route_name = '{0}.row.view'.format(self.route_prefix)
- # TODO: Fix this check for edit mode.
- edit_mode = self.request.referrer.endswith('/edit')
- if edit_mode and not batch.executed and self.request.has_perm('{0}.edit'.format(self.permission_prefix)):
- # g.editable = True
- # g.edit_route_name = '{0}.rows.edit'.format(self.route_prefix)
- g.deletable = True
- g.delete_route_name = '{0}.rows.delete'.format(self.route_prefix)
- return g
-
- def tr_class(self, row, i):
- pass
-
- def render_kwargs(self):
- """
- Add the current batch and route prefix to the template context.
- """
- return {'batch': self.current_batch(),
- 'route_prefix': self.route_prefix}
-
- def bulk_delete(self):
- """
- "Delete" all rows matching the current row grid view query. This sets
- the ``removed`` flag on the rows but does not truly delete them.
- """
- self.query().update({'removed': True}, synchronize_session=False)
- return httpexceptions.HTTPFound(location=self.request.route_url('{}.view'.format(self.route_prefix),
- uuid=self.request.matchdict['uuid']))
-
-
-class ProductBatchRowGrid(BatchRowGrid):
- """
- Base grid view for batch rows which deal directly with products.
- """
-
- def filter_map(self):
- """
- Provides the default filter map for batch row grid views. Derived
- classes should *not* override this, but :meth:`filter_map_extras()`
- instead.
- """
- return self.make_filter_map(exact=['status_code'],
- ilike=['brand_name', 'description', 'size'],
- upc=self.filter_gpc(self.row_class.upc))
-
- def filter_config(self):
- """
- Provides the default filter config for batch grid views. Derived
- classes should *not* override this, but :meth:`filter_config_extras()`
- instead.
- """
- kwargs = {'filter_label_status_code': "Status",
- 'filter_factory_status_code': EnumSearchFilter(self.row_class.STATUS),
- 'filter_label_upc': "UPC",
- 'filter_label_brand_name': "Brand"}
- kwargs.update(self.filter_config_extras())
- return self.make_filter_config(**kwargs)
-
-
class BatchRowCrud(BaseCrud):
"""
Base CRUD view for batch rows.
diff --git a/tailbone/views/custorders/orders.py b/tailbone/views/custorders/orders.py
index 6ef4f5f8..dc9990bf 100644
--- a/tailbone/views/custorders/orders.py
+++ b/tailbone/views/custorders/orders.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
@@ -33,7 +33,6 @@ from rattail.db import model
from tailbone import forms
from tailbone.db import Session
from tailbone.views import MasterView
-from tailbone.newgrids.filters import ChoiceValueRenderer
class CustomerOrdersView(MasterView):
diff --git a/tailbone/views/departments.py b/tailbone/views/departments.py
index f9aebf5b..37eed9ab 100644
--- a/tailbone/views/departments.py
+++ b/tailbone/views/departments.py
@@ -28,8 +28,8 @@ from __future__ import unicode_literals, absolute_import
from rattail.db import model
+from tailbone import newgrids as grids
from tailbone.views import MasterView, AutocompleteView
-from tailbone.newgrids import AlchemyGrid, GridAction
class DepartmentsView(MasterView):
@@ -67,11 +67,11 @@ class DepartmentsView(MasterView):
# shouldn't need a key for this one, for instance (no settings
# required), but there is plenty of room for improvement here.
employees = sorted(department.employees, key=unicode)
- employees = AlchemyGrid('departments.employees', self.request, data=employees, model_class=model.Employee,
- main_actions=[
- GridAction('view', icon='zoomin',
- url=lambda r, i: self.request.route_url('employees.view', uuid=r.uuid)),
- ])
+ employees = grids.AlchemyGrid('departments.employees', self.request, data=employees, model_class=model.Employee,
+ main_actions=[
+ grids.GridAction('view', icon='zoomin',
+ url=lambda r, i: self.request.route_url('employees.view', uuid=r.uuid)),
+ ])
employees.configure(include=[employees.display_name], readonly=True)
kwargs['employees'] = employees
diff --git a/tailbone/views/email.py b/tailbone/views/email.py
index 9da076e4..b6dca171 100644
--- a/tailbone/views/email.py
+++ b/tailbone/views/email.py
@@ -34,13 +34,12 @@ from rattail import mail
from rattail.db import api
from rattail.config import parse_list
-from tailbone import forms
+from tailbone import forms, newgrids as grids
from tailbone.db import Session
from tailbone.views import MasterView, View
-from tailbone.newgrids import Grid, GridColumn
-class BoolGridColumn(GridColumn):
+class BoolGridColumn(grids.GridColumn):
def render(self, value):
if value is None:
@@ -48,7 +47,7 @@ class BoolGridColumn(GridColumn):
return 'Yes' if value else 'No'
-class EmailListGridColumn(GridColumn):
+class EmailListGridColumn(grids.GridColumn):
def render(self, value):
if not value:
@@ -77,7 +76,7 @@ class ProfilesView(MasterView):
model_key = 'key'
url_prefix = '/email/profiles'
- grid_factory = Grid
+ grid_factory = grids.Grid
filterable = False
pageable = False
@@ -115,9 +114,9 @@ class ProfilesView(MasterView):
def configure_grid(self, g):
g.columns = [
- GridColumn('key'),
- GridColumn('prefix'),
- GridColumn('subject'),
+ grids.GridColumn('key'),
+ grids.GridColumn('prefix'),
+ grids.GridColumn('subject'),
EmailListGridColumn('to'),
BoolGridColumn('enabled'),
]
diff --git a/tailbone/views/employees.py b/tailbone/views/employees.py
index a4b86713..58962cc6 100644
--- a/tailbone/views/employees.py
+++ b/tailbone/views/employees.py
@@ -32,11 +32,9 @@ from rattail.db import model
import formalchemy as fa
-from tailbone import forms
+from tailbone import forms, newgrids as grids
from tailbone.db import Session
from tailbone.views import MasterView, AutocompleteView
-from tailbone.newgrids import AlchemyGrid, GridAction
-from tailbone.newgrids.filters import EnumValueRenderer
class EmployeesView(MasterView):
@@ -67,7 +65,7 @@ class EmployeesView(MasterView):
g.filters['status'].default_active = True
g.filters['status'].default_verb = 'equal'
g.filters['status'].default_value = self.enum.EMPLOYEE_STATUS_CURRENT
- g.filters['status'].set_value_renderer(EnumValueRenderer(self.enum.EMPLOYEE_STATUS))
+ g.filters['status'].set_value_renderer(grids.filters.EnumValueRenderer(self.enum.EMPLOYEE_STATUS))
else:
del g.filters['id']
del g.filters['status']
diff --git a/tailbone/views/grids/__init__.py b/tailbone/views/grids/__init__.py
deleted file mode 100644
index ba3106ed..00000000
--- a/tailbone/views/grids/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2012 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 .
-#
-################################################################################
-
-"""
-Grid Views
-"""
-
-from tailbone.views.grids.core import GridView
-from tailbone.views.grids.alchemy import (
- AlchemyGridView, SortableAlchemyGridView,
- PagedAlchemyGridView, SearchableAlchemyGridView)
diff --git a/tailbone/views/grids/alchemy.py b/tailbone/views/grids/alchemy.py
deleted file mode 100644
index ee564b8d..00000000
--- a/tailbone/views/grids/alchemy.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2014 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 .
-#
-################################################################################
-
-"""
-FormAlchemy Grid Views
-"""
-
-from webhelpers import paginate
-
-from .core import GridView
-from ... import grids
-from ...db import Session
-
-
-__all__ = ['AlchemyGridView', 'SortableAlchemyGridView',
- 'PagedAlchemyGridView', 'SearchableAlchemyGridView']
-
-
-class AlchemyGridView(GridView):
-
- def make_query(self, session=Session):
- query = session.query(self.mapped_class)
- return self.modify_query(query)
-
- def modify_query(self, query):
- return query
-
- def query(self):
- return self.make_query()
-
- def make_grid(self, **kwargs):
- self.update_grid_kwargs(kwargs)
- return grids.AlchemyGrid(
- self.request, self.mapped_class, self._data, **kwargs)
-
- def grid(self):
- return self.make_grid()
-
- def __call__(self):
- self._data = self.query()
- grid = self.grid()
- return self.render_grid(grid)
-
-
-class SortableAlchemyGridView(AlchemyGridView):
-
- sort = None
- full = True
-
- @property
- def config_prefix(self):
- raise NotImplementedError
-
- def join_map(self):
- return {}
-
- def make_sort_map(self, *args, **kwargs):
- return grids.util.get_sort_map(
- self.mapped_class, names=args or None, **kwargs)
-
- def sorter(self, field):
- return grids.util.sorter(field)
-
- def sort_map(self):
- return self.make_sort_map()
-
- def make_sort_config(self, **kwargs):
- return grids.util.get_sort_config(
- self.config_prefix, self.request, **kwargs)
-
- def sort_config(self):
- return self.make_sort_config(sort=self.sort)
-
- def modify_query(self, query):
- return grids.util.sort_query(
- query, self._sort_config, self.sort_map(), self.join_map())
-
- def make_grid(self, **kwargs):
- self.update_grid_kwargs(kwargs)
- return grids.AlchemyGrid(
- self.request, self.mapped_class, self._data,
- sort_map=self.sort_map(), config=self._sort_config, **kwargs)
-
- def grid(self):
- return self.make_grid()
-
- def __call__(self):
- self._sort_config = self.sort_config()
- self._data = self.query()
- grid = self.grid()
- return self.render_grid(grid)
-
-
-class PagedAlchemyGridView(SortableAlchemyGridView):
-
- def make_pager(self):
- config = self._sort_config
- query = self.query()
- return paginate.Page(
- query, item_count=query.count(),
- items_per_page=int(config['per_page']),
- page=int(config['page']),
- url=paginate.PageURL_WebOb(self.request))
-
- def __call__(self):
- self._sort_config = self.sort_config()
- self._data = self.make_pager()
- grid = self.grid()
- grid.pager = self._data
- return self.render_grid(grid)
-
-
-class SearchableAlchemyGridView(PagedAlchemyGridView):
-
- def filter_exact(self, field):
- return grids.search.filter_exact(field)
-
- def filter_ilike(self, field):
- return grids.search.filter_ilike(field)
-
- def filter_int(self, field):
- return grids.search.filter_int(field)
-
- def filter_soundex(self, field):
- return grids.search.filter_soundex(field)
-
- def filter_ilike_and_soundex(self, field):
- return grids.search.filter_ilike_and_soundex(field)
-
- def filter_gpc(self, field):
- return grids.search.filter_gpc(field)
-
- def make_filter_map(self, **kwargs):
- return grids.search.get_filter_map(self.mapped_class, **kwargs)
-
- def filter_map(self):
- return self.make_filter_map()
-
- def make_filter_config(self, **kwargs):
- return grids.search.get_filter_config(
- self.config_prefix, self.request, self.filter_map(), **kwargs)
-
- def filter_config(self):
- return self.make_filter_config()
-
- def make_search_form(self):
- return grids.search.get_search_form(
- self.request, self.filter_map(), self._filter_config)
-
- def search_form(self):
- return self.make_search_form()
-
- def modify_query(self, query):
- join_map = self.join_map()
- if not hasattr(self, '_filter_config'):
- self._filter_config = self.filter_config()
- query = grids.search.filter_query(
- query, self._filter_config, self.filter_map(), join_map)
- if hasattr(self, '_sort_config'):
- self._sort_config['joins'] = self._filter_config['joins']
- query = grids.util.sort_query(
- query, self._sort_config, self.sort_map(), join_map)
- return query
-
- def __call__(self):
- self._filter_config = self.filter_config()
- search = self.search_form()
- self._sort_config = self.sort_config()
- self._data = self.make_pager()
- grid = self.grid()
- grid.pager = self._data
- return self.render_grid(grid, search)
diff --git a/tailbone/views/grids/core.py b/tailbone/views/grids/core.py
deleted file mode 100644
index a2cfd48d..00000000
--- a/tailbone/views/grids/core.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2012 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 .
-#
-################################################################################
-
-"""
-Core Grid View
-"""
-
-from .. import View
-from ... import grids
-
-
-__all__ = ['GridView']
-
-
-class GridView(View):
-
- route_name = None
- route_url = None
- renderer = None
- permission = None
-
- full = False
- checkboxes = False
- deletable = False
-
- partial_only = False
-
- def update_grid_kwargs(self, kwargs):
- kwargs.setdefault('full', self.full)
- kwargs.setdefault('checkboxes', self.checkboxes)
- kwargs.setdefault('deletable', self.deletable)
- kwargs.setdefault('partial_only', self.partial_only)
-
- def make_grid(self, **kwargs):
- self.update_grid_kwargs(kwargs)
- return grids.Grid(self.request, **kwargs)
-
- def grid(self):
- return self.make_grid()
-
- def render_kwargs(self):
- return {}
-
- def render_grid(self, grid, search=None, **kwargs):
- kwargs = self.render_kwargs()
- kwargs['search_form'] = search
- return grids.util.render_grid(grid, **kwargs)
-
- def __call__(self):
- grid = self.grid()
- return self.render_grid(grid)
diff --git a/tailbone/views/purchasing/batch.py b/tailbone/views/purchasing/batch.py
index 1316b385..f2c3c31c 100644
--- a/tailbone/views/purchasing/batch.py
+++ b/tailbone/views/purchasing/batch.py
@@ -35,7 +35,7 @@ from rattail.time import localtime
import formalchemy as fa
from pyramid import httpexceptions
-from tailbone import forms, newgrids as grids
+from tailbone import forms
from tailbone.views.batch import BatchMasterView
diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py
index aed204cf..6c194296 100644
--- a/tailbone/views/roles.py
+++ b/tailbone/views/roles.py
@@ -35,10 +35,9 @@ import formalchemy as fa
from formalchemy.fields import IntegerFieldRenderer
from webhelpers.html import HTML, tags
-from tailbone import forms
+from tailbone import forms, newgrids as grids
from tailbone.db import Session
from tailbone.views.principal import PrincipalMasterView
-from tailbone.newgrids import AlchemyGrid, GridAction
class RolesView(PrincipalMasterView):
@@ -86,11 +85,11 @@ class RolesView(PrincipalMasterView):
# for this one, for instance (no settings required), but there is
# plenty of room for improvement here.
users = sorted(role.users, key=lambda u: u.username)
- users = AlchemyGrid('roles.users', self.request, data=users, model_class=model.User,
- main_actions=[
- GridAction('view', icon='zoomin',
- url=lambda r, i: self.request.route_url('users.view', uuid=r.uuid)),
- ])
+ users = grids.AlchemyGrid('roles.users', self.request, data=users, model_class=model.User,
+ main_actions=[
+ grids.GridAction('view', icon='zoomin',
+ url=lambda r, i: self.request.route_url('users.view', uuid=r.uuid)),
+ ])
users.configure(include=[users.username], readonly=True)
kwargs['users'] = users
diff --git a/tailbone/views/tables.py b/tailbone/views/tables.py
index 379df0b5..c3c1dc3e 100644
--- a/tailbone/views/tables.py
+++ b/tailbone/views/tables.py
@@ -26,8 +26,8 @@ Views with info about the underlying Rattail tables
from __future__ import unicode_literals, absolute_import
+from tailbone import newgrids as grids
from tailbone.views import MasterView
-from tailbone.newgrids import Grid, GridColumn
class TablesView(MasterView):
@@ -41,7 +41,7 @@ class TablesView(MasterView):
editable = False
deletable = False
viewable = False
- grid_factory = Grid
+ grid_factory = grids.Grid
filterable = False
pageable = False
@@ -59,8 +59,8 @@ class TablesView(MasterView):
def configure_grid(self, g):
g.columns = [
- GridColumn('name'),
- GridColumn('row_count'),
+ grids.GridColumn('name'),
+ grids.GridColumn('row_count'),
]
g.sorters['name'] = g.make_sorter('name', foldcase=True)