Add initial versioning support with SQLAlchemy-Continuum.
This commit is contained in:
		
							parent
							
								
									41dd2ef17b
								
							
						
					
					
						commit
						def466935b
					
				
					 43 changed files with 717 additions and 26 deletions
				
			
		|  | @ -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,15 +20,18 @@ | |||
| #  along with Rattail.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ################################################################################ | ||||
| 
 | ||||
| """ | ||||
| Brand Views | ||||
| """ | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView, AutocompleteView | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import Brand | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView, AutocompleteView | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class BrandsGrid(SearchableAlchemyGridView): | ||||
| 
 | ||||
|  | @ -81,6 +83,14 @@ class BrandCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class BrandVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a brand. | ||||
|     """ | ||||
|     parent_class = model.Brand | ||||
|     route_model_view = 'brand.read' | ||||
| 
 | ||||
| 
 | ||||
| class BrandsAutocomplete(AutocompleteView): | ||||
| 
 | ||||
|     mapped_class = Brand | ||||
|  | @ -122,3 +132,5 @@ def includeme(config): | |||
|     config.add_view(BrandCrud, attr='delete', | ||||
|                     route_name='brand.delete', | ||||
|                     permission='brands.delete') | ||||
| 
 | ||||
|     version_defaults(config, BrandVersionView, 'brand') | ||||
|  |  | |||
|  | @ -26,9 +26,11 @@ Category Views | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import Category | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class CategoriesGrid(SearchableAlchemyGridView): | ||||
|  | @ -85,6 +87,16 @@ class CategoryCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class CategoryVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a category. | ||||
|     """ | ||||
|     parent_class = model.Category | ||||
|     model_title_plural = "Categories" | ||||
|     route_model_list = 'categories' | ||||
|     route_model_view = 'category.read' | ||||
| 
 | ||||
| 
 | ||||
| def add_routes(config): | ||||
|     config.add_route('categories',      '/categories') | ||||
|     config.add_route('category.create', '/categories/new') | ||||
|  | @ -109,3 +121,5 @@ def includeme(config): | |||
|                     renderer='/categories/crud.mako', permission='categories.update') | ||||
|     config.add_view(CategoryCrud, attr='delete', route_name='category.delete', | ||||
|                     permission='categories.delete') | ||||
| 
 | ||||
|     version_defaults(config, CategoryVersionView, 'category', template_prefix='/categories') | ||||
|  |  | |||
							
								
								
									
										231
									
								
								tailbone/views/continuum.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								tailbone/views/continuum.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,231 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| ################################################################################ | ||||
| # | ||||
| #  Rattail -- Retail Software Framework | ||||
| #  Copyright © 2010-2015 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 <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ################################################################################ | ||||
| """ | ||||
| Continuum Version Views | ||||
| """ | ||||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| import sqlalchemy as sa | ||||
| import sqlalchemy_continuum as continuum | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model.continuum import model_transaction_query | ||||
| 
 | ||||
| import formalchemy | ||||
| from pyramid.httpexceptions import HTTPNotFound | ||||
| 
 | ||||
| from tailbone.db import Session | ||||
| from tailbone.views import PagedAlchemyGridView, View | ||||
| from tailbone.forms import DateTimeFieldRenderer | ||||
| 
 | ||||
| 
 | ||||
| class VersionView(PagedAlchemyGridView): | ||||
|     """ | ||||
|     View which shows version history for a model instance. | ||||
|     """ | ||||
| 
 | ||||
|     @property | ||||
|     def parent_class(self): | ||||
|         """ | ||||
|         Model class which is "parent" to the version class. | ||||
|         """ | ||||
|         raise NotImplementedError("Please set `parent_class` on your `VersionView` subclass.") | ||||
| 
 | ||||
|     @property | ||||
|     def child_classes(self): | ||||
|         """ | ||||
|         Model class(es) which are "children" to the version's parent class. | ||||
|         """ | ||||
|         return [] | ||||
| 
 | ||||
|     @property | ||||
|     def model_title(self): | ||||
|         """ | ||||
|         Human-friendly title for the parent model class. | ||||
|         """ | ||||
|         return self.parent_class.__name__ | ||||
| 
 | ||||
|     @property | ||||
|     def model_title_plural(self): | ||||
|         """ | ||||
|         Plural version of the human-friendly title for the parent model class. | ||||
|         """ | ||||
|         return '{0}s'.format(self.model_title) | ||||
| 
 | ||||
|     @property | ||||
|     def prefix(self): | ||||
|         return self.parent_class.__name__.lower() | ||||
| 
 | ||||
|     @property | ||||
|     def config_prefix(self): | ||||
|         return self.prefix | ||||
| 
 | ||||
|     @property | ||||
|     def transaction_class(self): | ||||
|         return continuum.transaction_class(self.parent_class) | ||||
| 
 | ||||
|     @property | ||||
|     def mapped_class(self): | ||||
|         return self.transaction_class | ||||
| 
 | ||||
|     @property | ||||
|     def version_class(self): | ||||
|         return continuum.version_class(self.parent_class) | ||||
| 
 | ||||
|     @property | ||||
|     def route_model_list(self): | ||||
|         return '{0}s'.format(self.prefix) | ||||
| 
 | ||||
|     @property | ||||
|     def route_model_view(self): | ||||
|         return self.prefix | ||||
| 
 | ||||
|     def join_map(self): | ||||
|         return { | ||||
|             'user': | ||||
|                 lambda q: q.outerjoin(model.User, self.transaction_class.user_uuid == model.User.uuid), | ||||
|             } | ||||
| 
 | ||||
|     def sort_config(self): | ||||
|         return self.make_sort_config(sort='issued_at', dir='desc') | ||||
| 
 | ||||
|     def sort_map(self): | ||||
|         return self.make_sort_map('issued_at', 'remote_addr', | ||||
|             user=self.sorter(model.User.username)) | ||||
| 
 | ||||
|     def transaction_query(self, session=Session): | ||||
|         uuid = self.request.matchdict['uuid'] | ||||
|         return model_transaction_query(session, uuid, self.parent_class, | ||||
|                                        child_classes=self.child_classes) | ||||
| 
 | ||||
|     def make_query(self, session=Session): | ||||
|         query = self.transaction_query(session) | ||||
|         return self.modify_query(query) | ||||
| 
 | ||||
|     def grid(self): | ||||
|         g = self.make_grid() | ||||
|         g.issued_at.set(renderer=DateTimeFieldRenderer(self.request.rattail_config)) | ||||
|         g.configure( | ||||
|             include=[ | ||||
|                 g.issued_at.label("When"), | ||||
|                 g.user.label("Who"), | ||||
|                 g.remote_addr.label("Client IP"), | ||||
|                 ], | ||||
|             readonly=True) | ||||
|         g.viewable = True | ||||
|         g.view_route_name = '{0}.version'.format(self.prefix) | ||||
|         g.view_route_kwargs = self.view_route_kwargs | ||||
|         return g | ||||
| 
 | ||||
|     def render_kwargs(self): | ||||
|         instance = Session.query(self.parent_class).get(self.request.matchdict['uuid']) | ||||
|         return {'model_title': self.model_title, | ||||
|                 'model_title_plural': self.model_title_plural, | ||||
|                 'model_instance': instance, | ||||
|                 'route_model_list': self.route_model_list, | ||||
|                 'route_model_view': self.route_model_view} | ||||
| 
 | ||||
|     def view_route_kwargs(self, transaction): | ||||
|         return {'uuid': self.request.matchdict['uuid'], | ||||
|                 'transaction_id': transaction.id} | ||||
| 
 | ||||
|     def list(self): | ||||
|         """ | ||||
|         View which shows the version history list for a model instance. | ||||
|         """ | ||||
|         return self() | ||||
| 
 | ||||
|     def details(self): | ||||
|         """ | ||||
|         View which shows the change details of a model version. | ||||
|         """ | ||||
|         kwargs = self.render_kwargs() | ||||
|         uuid = self.request.matchdict['uuid'] | ||||
|         transaction_id = self.request.matchdict['transaction_id'] | ||||
|         transaction = Session.query(self.transaction_class).get(transaction_id) | ||||
|         if not transaction: | ||||
|             raise HTTPNotFound | ||||
| 
 | ||||
|         version = Session.query(self.version_class).get((uuid, transaction_id)) | ||||
| 
 | ||||
|         def normalize_child_classes(): | ||||
|             classes = [] | ||||
|             for cls in self.child_classes: | ||||
|                 if not isinstance(cls, tuple): | ||||
|                     cls = (cls, 'uuid') | ||||
|                 classes.append(cls) | ||||
|             return classes | ||||
| 
 | ||||
|         versions = [] | ||||
|         if version: | ||||
|             versions.append(version) | ||||
|         for model_class, attr in normalize_child_classes(): | ||||
|             if isinstance(model_class, type) and issubclass(model_class, model.Base): | ||||
|                 cls = continuum.version_class(model_class) | ||||
|                 ver = Session.query(cls).filter_by(transaction_id=transaction_id, **{attr: uuid}).first() | ||||
|                 if ver: | ||||
|                     versions.append(ver) | ||||
| 
 | ||||
|         previous_transaction = self.transaction_query()\ | ||||
|             .order_by(self.transaction_class.id.desc())\ | ||||
|             .filter(self.transaction_class.id < transaction.id)\ | ||||
|             .first() | ||||
| 
 | ||||
|         next_transaction = self.transaction_query()\ | ||||
|             .order_by(self.transaction_class.id.asc())\ | ||||
|             .filter(self.transaction_class.id > transaction.id)\ | ||||
|             .first() | ||||
| 
 | ||||
|         kwargs.update({ | ||||
|                 'route_prefix': self.prefix, | ||||
|                 'version': version, | ||||
|                 'transaction': transaction, | ||||
|                 'versions': versions, | ||||
|                 'parent_class': continuum.parent_class, | ||||
|                 'previous_transaction': previous_transaction, | ||||
|                 'next_transaction': next_transaction, | ||||
|                 }) | ||||
| 
 | ||||
|         return kwargs | ||||
| 
 | ||||
| 
 | ||||
| def version_defaults(config, VersionView, prefix, template_prefix=None): | ||||
|     """ | ||||
|     Apply default route/view configuration for the given ``VersionView``. | ||||
|     """ | ||||
|     if template_prefix is None: | ||||
|         template_prefix = '/{0}s'.format(prefix) | ||||
|     template_prefix = template_prefix.rstrip('/') | ||||
| 
 | ||||
|     # list changesets | ||||
|     config.add_route('{0}.versions'.format(prefix), '/{0}/{{uuid}}/changesets/'.format(prefix)) | ||||
|     config.add_view(VersionView, attr='list', route_name='{0}.versions'.format(prefix), | ||||
|                     renderer='{0}/versions/index.mako'.format(template_prefix), | ||||
|                     permission='{0}.versions.view'.format(prefix)) | ||||
| 
 | ||||
|     # view changeset | ||||
|     config.add_route('{0}.version'.format(prefix), '/{0}/{{uuid}}/changeset/{{transaction_id}}'.format(prefix)) | ||||
|     config.add_view(VersionView, attr='details', route_name='{0}.version'.format(prefix), | ||||
|                     renderer='{0}/versions/view.mako'.format(template_prefix), | ||||
|                     permission='{0}.versions.view'.format(prefix)) | ||||
|  | @ -32,16 +32,23 @@ except ImportError: | |||
|     inspect = None | ||||
|     from sqlalchemy.orm import class_mapper | ||||
| 
 | ||||
| import sqlalchemy as sa | ||||
| from sqlalchemy_continuum import transaction_class, version_class | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model.continuum import count_versions, model_transaction_query | ||||
| 
 | ||||
| from pyramid.httpexceptions import HTTPFound, HTTPNotFound | ||||
| 
 | ||||
| from .core import View | ||||
| 
 | ||||
| from ..forms import AlchemyForm | ||||
| from formalchemy import FieldSet | ||||
| from ..db import Session | ||||
| 
 | ||||
| from edbob.util import prettify | ||||
| 
 | ||||
| from tailbone.db import Session | ||||
| 
 | ||||
| 
 | ||||
| __all__ = ['CrudView'] | ||||
| 
 | ||||
|  | @ -51,6 +58,7 @@ class CrudView(View): | |||
|     readonly = False | ||||
|     allow_successive_creates = False | ||||
|     update_cancel_route = None | ||||
|     child_version_classes = [] | ||||
| 
 | ||||
|     @property | ||||
|     def mapped_class(self): | ||||
|  | @ -161,7 +169,21 @@ class CrudView(View): | |||
|         pass | ||||
| 
 | ||||
|     def template_kwargs(self, form): | ||||
|         return {} | ||||
|         if form.creating: | ||||
|             return {} | ||||
|         return {'version_count': self.count_versions()} | ||||
| 
 | ||||
|     def count_versions(self): | ||||
|         query = self.transaction_query() | ||||
|         return query.count() | ||||
| 
 | ||||
|     def transaction_query(self, parent_class=None, child_classes=None): | ||||
|         uuid = self.request.matchdict['uuid'] | ||||
|         if parent_class is None: | ||||
|             parent_class = self.mapped_class | ||||
|         if child_classes is None: | ||||
|             child_classes = self.child_version_classes | ||||
|         return model_transaction_query(Session, uuid, parent_class, child_classes=child_classes) | ||||
| 
 | ||||
|     def post_save(self, form): | ||||
|         pass | ||||
|  |  | |||
|  | @ -26,9 +26,11 @@ Department Views | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import Department, Product, ProductCost, Vendor | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView, AlchemyGridView, AutocompleteView | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class DepartmentsGrid(SearchableAlchemyGridView): | ||||
|  | @ -83,6 +85,14 @@ class DepartmentCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class DepartmentVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a department. | ||||
|     """ | ||||
|     parent_class = model.Department | ||||
|     route_model_view = 'department.read' | ||||
| 
 | ||||
| 
 | ||||
| class DepartmentsByVendorGrid(AlchemyGridView): | ||||
| 
 | ||||
|     mapped_class = Department | ||||
|  | @ -150,3 +160,5 @@ def includeme(config): | |||
|                     renderer='/departments/crud.mako', permission='departments.update') | ||||
|     config.add_view(DepartmentCrud, attr='delete', route_name='department.delete', | ||||
|                     permission='departments.delete') | ||||
| 
 | ||||
|     version_defaults(config, DepartmentVersionView, 'department') | ||||
|  |  | |||
|  | @ -26,6 +26,9 @@ Label Views | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import LabelProfile | ||||
| 
 | ||||
| from pyramid.httpexceptions import HTTPFound | ||||
| 
 | ||||
| import formalchemy | ||||
|  | @ -36,7 +39,7 @@ from ..db import Session | |||
| from . import SearchableAlchemyGridView, CrudView | ||||
| from ..grids.search import BooleanSearchFilter | ||||
| 
 | ||||
| from rattail.db.model import LabelProfile | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class ProfilesGrid(SearchableAlchemyGridView): | ||||
|  | @ -129,6 +132,16 @@ class ProfileCrud(CrudView): | |||
|                                       uuid=form.fieldset.model.uuid) | ||||
| 
 | ||||
| 
 | ||||
| class LabelProfileVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a label profile. | ||||
|     """ | ||||
|     parent_class = model.LabelProfile | ||||
|     model_title = "Label Profile" | ||||
|     route_model_list = 'label_profiles' | ||||
|     route_model_view = 'label_profile.read' | ||||
| 
 | ||||
| 
 | ||||
| def printer_settings(request): | ||||
|     uuid = request.matchdict['uuid'] | ||||
|     profile = Session.query(LabelProfile).get(uuid) if uuid else None | ||||
|  | @ -187,3 +200,5 @@ def includeme(config): | |||
|     config.add_view(printer_settings, route_name='label_profile.printer_settings', | ||||
|                     renderer='/labels/profiles/printer.mako', | ||||
|                     permission='label_profiles.update') | ||||
| 
 | ||||
|     version_defaults(config, LabelProfileVersionView, 'labelprofile', template_prefix='/labels/profiles') | ||||
|  |  | |||
|  | @ -56,6 +56,7 @@ from rattail.pod import get_image_url, get_image_path | |||
| from ..db import Session | ||||
| from ..forms import GPCFieldRenderer, BrandFieldRenderer, PriceFieldRenderer | ||||
| from . import CrudView | ||||
| from .continuum import VersionView, version_defaults | ||||
| from ..progress import SessionProgress | ||||
| 
 | ||||
| 
 | ||||
|  | @ -237,9 +238,16 @@ class ProductsGrid(SearchableAlchemyGridView): | |||
| 
 | ||||
| 
 | ||||
| class ProductCrud(CrudView): | ||||
| 
 | ||||
|     """ | ||||
|     Product CRUD view class. | ||||
|     """ | ||||
|     mapped_class = Product | ||||
|     home_route = 'products' | ||||
|     child_version_classes = [ | ||||
|         (model.ProductCode, 'product_uuid'), | ||||
|         (model.ProductCost, 'product_uuid'), | ||||
|         (model.ProductPrice, 'product_uuid'), | ||||
|         ] | ||||
| 
 | ||||
|     def get_model(self, key): | ||||
|         model = super(ProductCrud, self).get_model(key) | ||||
|  | @ -277,7 +285,8 @@ class ProductCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
|     def template_kwargs(self, form): | ||||
|         kwargs = {'image': False} | ||||
|         kwargs = super(ProductCrud, self).template_kwargs(form) | ||||
|         kwargs['image'] = False | ||||
|         product = form.fieldset.model | ||||
|         if product.upc: | ||||
|             kwargs['image_url'] = get_image_url( | ||||
|  | @ -289,6 +298,19 @@ class ProductCrud(CrudView): | |||
|         return kwargs | ||||
| 
 | ||||
| 
 | ||||
| class ProductVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a product. | ||||
|     """ | ||||
|     parent_class = model.Product | ||||
|     route_model_view = 'product.read' | ||||
|     child_classes = [ | ||||
|         (model.ProductCode, 'product_uuid'), | ||||
|         (model.ProductCost, 'product_uuid'), | ||||
|         (model.ProductPrice, 'product_uuid'), | ||||
|         ] | ||||
| 
 | ||||
| 
 | ||||
| def products_search(request): | ||||
|     """ | ||||
|     Locate a product(s) by UPC. | ||||
|  | @ -443,3 +465,5 @@ def includeme(config): | |||
|                     permission='products.delete') | ||||
|     config.add_view(products_search, route_name='products.search', | ||||
|                     renderer='json', permission='products.list') | ||||
| 
 | ||||
|     version_defaults(config, ProductVersionView, 'product') | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ | |||
| Role Views | ||||
| """ | ||||
| 
 | ||||
| from rattail.db import model | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView | ||||
| from pyramid.httpexceptions import HTTPFound | ||||
| 
 | ||||
|  | @ -37,6 +39,8 @@ import formalchemy | |||
| from webhelpers.html import tags | ||||
| from webhelpers.html import HTML | ||||
| 
 | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| default_permissions = [ | ||||
|     ("Batches", [ | ||||
|  | @ -264,6 +268,14 @@ class RoleCrud(CrudView): | |||
|             return HTTPFound(location=self.request.get_referrer()) | ||||
| 
 | ||||
| 
 | ||||
| class RoleVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a role. | ||||
|     """ | ||||
|     parent_class = model.Role | ||||
|     route_model_view = 'role.read' | ||||
| 
 | ||||
| 
 | ||||
| def includeme(config): | ||||
|      | ||||
|     config.add_route('roles', '/roles') | ||||
|  | @ -294,3 +306,5 @@ def includeme(config): | |||
|     config.add_route('role.delete', '/roles/{uuid}/delete') | ||||
|     config.add_view(RoleCrud, attr='delete', route_name='role.delete', | ||||
|                     permission='roles.delete') | ||||
| 
 | ||||
|     version_defaults(config, RoleVersionView, 'role') | ||||
|  |  | |||
|  | @ -26,9 +26,11 @@ Subdepartment Views | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import Subdepartment | ||||
| 
 | ||||
| from . import SearchableAlchemyGridView, CrudView | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class SubdepartmentsGrid(SearchableAlchemyGridView): | ||||
|  | @ -85,6 +87,14 @@ class SubdepartmentCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class SubdepartmentVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a subdepartment. | ||||
|     """ | ||||
|     parent_class = model.Subdepartment | ||||
|     route_model_view = 'subdepartment.read' | ||||
| 
 | ||||
| 
 | ||||
| def add_routes(config): | ||||
|     config.add_route('subdepartments',          '/subdepartments') | ||||
|     config.add_route('subdepartment.create',    '/subdepartments/new') | ||||
|  | @ -109,3 +119,5 @@ def includeme(config): | |||
|                     renderer='/subdepartments/crud.mako', permission='subdepartments.update') | ||||
|     config.add_view(SubdepartmentCrud, attr='delete', route_name='subdepartment.delete', | ||||
|                     permission='subdepartments.delete') | ||||
| 
 | ||||
|     version_defaults(config, SubdepartmentVersionView, 'subdepartment') | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ User Views | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import User, Person, Role | ||||
| from rattail.db.auth import guest_role, set_user_password | ||||
| 
 | ||||
|  | @ -40,6 +41,8 @@ from ..forms import PersonFieldLinkRenderer | |||
| from ..db import Session | ||||
| from tailbone.grids.search import BooleanSearchFilter | ||||
| 
 | ||||
| from .continuum import VersionView, version_defaults | ||||
| 
 | ||||
| 
 | ||||
| class UsersGrid(SearchableAlchemyGridView): | ||||
| 
 | ||||
|  | @ -205,6 +208,14 @@ class UserCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class UserVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a user. | ||||
|     """ | ||||
|     parent_class = model.User | ||||
|     route_model_view = 'user.read' | ||||
| 
 | ||||
| 
 | ||||
| def add_routes(config): | ||||
|     config.add_route(u'users',          u'/users') | ||||
|     config.add_route(u'user.create',    u'/users/new') | ||||
|  | @ -233,3 +244,5 @@ def includeme(config): | |||
|                     permission='users.update') | ||||
|     config.add_view(UserCrud, attr='delete', route_name='user.delete', | ||||
|                     permission='users.delete') | ||||
| 
 | ||||
|     version_defaults(config, UserVersionView, 'user') | ||||
|  |  | |||
							
								
								
									
										3
									
								
								tailbone/views/vendors/__init__.py
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								tailbone/views/vendors/__init__.py
									
										
									
									
										vendored
									
									
								
							|  | @ -26,7 +26,8 @@ Views pertaining to vendors | |||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from .core import VendorsGrid, VendorCrud, VendorsAutocomplete, add_routes | ||||
| from .core import (VendorsGrid, VendorCrud, VendorVersionView, | ||||
|                    VendorsAutocomplete, add_routes) | ||||
| 
 | ||||
| 
 | ||||
| def includeme(config): | ||||
|  |  | |||
							
								
								
									
										25
									
								
								tailbone/views/vendors/core.py
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								tailbone/views/vendors/core.py
									
										
									
									
										vendored
									
									
								
							|  | @ -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,15 +20,19 @@ | |||
| #  along with Rattail.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ################################################################################ | ||||
| 
 | ||||
| """ | ||||
| Vendor Views | ||||
| """ | ||||
| 
 | ||||
| from tailbone.views import SearchableAlchemyGridView, CrudView, AutocompleteView | ||||
| from tailbone.forms import AssociationProxyField, PersonFieldRenderer | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from rattail.db import model | ||||
| from rattail.db.model import Vendor | ||||
| 
 | ||||
| from tailbone.views import SearchableAlchemyGridView, CrudView, AutocompleteView | ||||
| from tailbone.views.continuum import VersionView, version_defaults | ||||
| from tailbone.forms import AssociationProxyField, PersonFieldRenderer | ||||
| 
 | ||||
| 
 | ||||
| class VendorsGrid(SearchableAlchemyGridView): | ||||
| 
 | ||||
|  | @ -93,6 +96,14 @@ class VendorCrud(CrudView): | |||
|         return fs | ||||
| 
 | ||||
| 
 | ||||
| class VendorVersionView(VersionView): | ||||
|     """ | ||||
|     View which shows version history for a vendor. | ||||
|     """ | ||||
|     parent_class = model.Vendor | ||||
|     route_model_view = 'vendor.read' | ||||
| 
 | ||||
| 
 | ||||
| class VendorsAutocomplete(AutocompleteView): | ||||
| 
 | ||||
|     mapped_class = Vendor | ||||
|  | @ -127,3 +138,5 @@ def includeme(config): | |||
|                     permission='vendors.update') | ||||
|     config.add_view(VendorCrud, attr='delete', route_name='vendor.delete', | ||||
|                     permission='vendors.delete') | ||||
| 
 | ||||
|     version_defaults(config, VendorVersionView, 'vendor') | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar