Add basic "mobile index" master view, plus support for demo mode

This commit is contained in:
Lance Edgar 2017-03-19 11:21:00 -05:00
parent 9808bb3a91
commit 581a21bd9d
16 changed files with 301 additions and 16 deletions

View file

@ -38,7 +38,7 @@ from webhelpers.html import HTML, tags
from tailbone import forms, newgrids as grids
from tailbone.views import View
from tailbone.newgrids import filters, AlchemyGrid, GridAction
from tailbone.newgrids import filters, AlchemyGrid, GridAction, MobileGrid
class MasterView(View):
@ -57,6 +57,8 @@ class MasterView(View):
bulk_deletable = False
mergeable = False
supports_mobile = False
listing = False
creating = False
viewing = False
@ -122,6 +124,83 @@ class MasterView(View):
return self.render_to_response('index', {'grid': grid})
def mobile_index(self):
"""
Mobile "home" page for the data model
"""
self.listing = True
grid = self.make_mobile_grid()
return self.render_to_response('index', {'grid': grid}, mobile=True)
def make_mobile_grid(self, **kwargs):
factory = self.get_mobile_grid_factory()
key = self.get_mobile_grid_key()
data = self.get_mobile_data(session=kwargs.get('session'))
kwargs = self.make_mobile_grid_kwargs(**kwargs)
kwargs.setdefault('key', key)
kwargs.setdefault('request', self.request)
kwargs.setdefault('data', data)
kwargs.setdefault('model_class', self.get_model_class(error=False))
grid = factory(**kwargs)
self.preconfigure_mobile_grid(grid)
self.configure_mobile_grid(grid)
return grid
@classmethod
def get_mobile_grid_factory(cls):
"""
Must return a callable to be used when creating new mobile grid
instances. Instead of overriding this, you can set
:attr:`mobile_grid_factory`. Default factory is :class:`MobileGrid`.
"""
return getattr(cls, 'mobile_grid_factory', MobileGrid)
@classmethod
def get_mobile_grid_key(cls):
"""
Must return a unique "config key" for the mobile grid, for sort/filter
purposes etc. (It need only be unique among *mobile* grids.) Instead
of overriding this, you can set :attr:`mobile_grid_key`. Default is
the value returned by :meth:`get_route_prefix()`.
"""
if hasattr(cls, 'mobile_grid_key'):
return cls.mobile_grid_key
return cls.get_route_prefix()
def get_mobile_data(self, session=None):
"""
Must return the "raw" / full data set for the mobile grid. This data
should *not* yet be sorted or filtered in any way; that happens later.
Default is the value returned by :meth:`get_data()`, in which case all
records visible in the traditional view, are visible in mobile too.
"""
return self.get_data(session=session)
def make_mobile_grid_kwargs(self, **kwargs):
"""
Must return a dictionary of kwargs to be passed to the factory when
creating new mobile grid instances.
"""
defaults = {
'route_prefix': self.get_route_prefix(),
}
defaults.update(kwargs)
return defaults
def preconfigure_mobile_grid(self, grid):
"""
Optionally perform pre-configuration for the mobile grid, to establish
some sane defaults etc.
"""
def configure_mobile_grid(self, grid):
"""
Configure the mobile grid. The primary objective here is to define
which columns to show and in which order etc. Along the way you're
free to customize any column(s) you like, as needed.
"""
grid.configure()
def create(self):
"""
View for creating a new model record.
@ -185,6 +264,23 @@ class MasterView(View):
tools=self.make_row_grid_tools(instance))
return self.render_to_response('view', context)
def mobile_view(self):
"""
Mobile view for displaying a single object's details
"""
self.viewing = True
instance = self.get_instance()
# form = self.make_form(instance)
context = {
'instance': instance,
'instance_title': self.get_instance_title(instance),
# 'instance_editable': self.editable_instance(instance),
# 'instance_deletable': self.deletable_instance(instance),
# 'form': form,
}
return self.render_to_response('view', context, mobile=True)
def make_default_row_grid_tools(self, obj):
if self.rows_creatable:
link = tags.link_to("Create a new {}".format(self.get_row_model_title()),
@ -614,7 +710,7 @@ class MasterView(View):
return self.request.route_url('{0}.{1}'.format(self.get_route_prefix(), action),
**self.get_action_route_kwargs(instance))
def render_to_response(self, template, data):
def render_to_response(self, template, data, mobile=False):
"""
Return a response with the given template rendered with the given data.
Note that ``template`` must only be a "key" (e.g. 'index' or 'view').
@ -650,14 +746,17 @@ class MasterView(View):
context.update(getattr(self, 'template_kwargs_{}'.format(template))(**context))
# First try the template path most specific to the view.
if mobile:
mako_path = '/mobile{}/{}.mako'.format(self.get_template_prefix(), template)
else:
mako_path = '{}/{}.mako'.format(self.get_template_prefix(), template)
try:
return render_to_response('{}/{}.mako'.format(self.get_template_prefix(), template),
context, request=self.request)
return render_to_response(mako_path, context, request=self.request)
except IOError:
# Failing that, try one or more fallback templates.
for fallback in self.get_fallback_templates(template):
for fallback in self.get_fallback_templates(template, mobile=mobile):
try:
return render_to_response(fallback, context, request=self.request)
except IOError:
@ -704,7 +803,9 @@ class MasterView(View):
return render('{}/{}.mako'.format(self.get_template_prefix(), template),
context, request=self.request)
def get_fallback_templates(self, template):
def get_fallback_templates(self, template, mobile=False):
if mobile:
return ['/mobile/master/{}.mako'.format(template)]
return ['/master/{}.mako'.format(template)]
def template_kwargs(self, **kwargs):
@ -1297,11 +1398,15 @@ class MasterView(View):
# list/search
if cls.listable:
config.add_tailbone_permission(permission_prefix, '{}.list'.format(permission_prefix),
"List / search {}".format(model_title_plural))
config.add_route(route_prefix, '{}/'.format(url_prefix))
config.add_view(cls, attr='index', route_name=route_prefix,
permission='{}.list'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{}.list'.format(permission_prefix),
"List / search {}".format(model_title_plural))
if cls.supports_mobile:
config.add_route('mobile.{}'.format(route_prefix), '/mobile{}/'.format(url_prefix))
config.add_view(cls, attr='mobile_index', route_name='mobile.{}'.format(route_prefix),
permission='{}.list'.format(permission_prefix))
# create
if cls.creatable:
@ -1344,6 +1449,10 @@ class MasterView(View):
config.add_route('{}.view'.format(route_prefix), '{}/{{{}}}'.format(url_prefix, model_key))
config.add_view(cls, attr='view', route_name='{}.view'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
if cls.supports_mobile:
config.add_route('mobile.{}.view'.format(route_prefix), '/mobile{}/{{{}}}'.format(url_prefix, model_key))
config.add_view(cls, attr='mobile_view', route_name='mobile.{}.view'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
# edit
if cls.editable: