Add basic "DB picker" support, for views which allow multiple engines

i.e. whichever engine is "current" will determine where data comes from
This commit is contained in:
Lance Edgar 2019-07-09 22:14:12 -05:00
parent 0d7492f6be
commit 839f6affe2
3 changed files with 92 additions and 3 deletions

View file

@ -132,6 +132,21 @@
% endif % endif
</div> </div>
% if expose_db_picker is not Undefined and expose_db_picker:
<div class="level-item">
<p>DB:</p>
</div>
<div class="level-item">
${h.form(url('change_db_engine'))}
${h.csrf_token(request)}
${h.hidden('engine_type', value=master.engine_type_key)}
<div class="select">
${h.select('dbkey', db_picker_selected, db_picker_options, id='db-picker')}
</div>
${h.end_form()}
</div>
% endif
</div><!-- level-left --> </div><!-- level-left -->
<div class="level-right"> <div class="level-right">
@ -303,6 +318,13 @@
var session_timeout = ${request.get_session_timeout() or 'null'}; var session_timeout = ${request.get_session_timeout() or 'null'};
var logout_url = '${request.route_url('logout')}'; var logout_url = '${request.route_url('logout')}';
var noop_url = '${request.route_url('noop')}'; var noop_url = '${request.route_url('noop')}';
% if expose_db_picker is not Undefined and expose_db_picker:
$(function() {
$('#db-picker').change(function() {
$(this).parents('form:first').submit();
});
});
% endif
% if expose_theme_picker and request.has_perm('common.change_app_theme'): % if expose_theme_picker and request.has_perm('common.change_app_theme'):
$(function() { $(function() {
$('#theme-picker').change(function() { $('#theme-picker').change(function() {

View file

@ -143,6 +143,20 @@ class CommonView(View):
self.request.session.flash("App theme has been changed to: {}".format(theme)) self.request.session.flash("App theme has been changed to: {}".format(theme))
return self.redirect(self.request.get_referrer()) return self.redirect(self.request.get_referrer())
def change_db_engine(self):
"""
Simple view which can change user's "current" database engine, of a
given type, then redirect back to referring page.
"""
engine_type = self.request.POST.get('engine_type')
if engine_type:
dbkey = self.request.POST.get('dbkey')
if dbkey:
self.request.session['tailbone.engines.{}.current'.format(engine_type)] = dbkey
if self.rattail_config.getbool('tailbone', 'engines.flash_after_change', default=True):
self.request.session.flash("Switched '{}' database to: {}".format(engine_type, dbkey))
return self.redirect(self.request.get_referrer())
def feedback(self): def feedback(self):
""" """
Generic view to handle the user feedback form. Generic view to handle the user feedback form.
@ -212,6 +226,12 @@ class CommonView(View):
config.add_route('mobile.about', '/mobile/about') config.add_route('mobile.about', '/mobile/about')
config.add_view(cls, attr='about', route_name='mobile.about', renderer='/mobile/about.mako') config.add_view(cls, attr='about', route_name='mobile.about', renderer='/mobile/about.mako')
# change db engine
config.add_tailbone_permission('common', 'common.change_db_engine',
"Change which Database Engine is active (for user)")
config.add_route('change_db_engine', '/change-db-engine', request_method='POST')
config.add_view(cls, attr='change_db_engine', route_name='change_db_engine')
# change theme # change theme
config.add_tailbone_permission('common', 'common.change_app_theme', config.add_tailbone_permission('common', 'common.change_app_theme',
"Change global App Template Theme") "Change global App Template Theme")

View file

@ -36,6 +36,7 @@ import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
import sqlalchemy_continuum as continuum import sqlalchemy_continuum as continuum
from sqlalchemy_utils.functions import get_primary_keys
from rattail.db import model, Session as RattailSession from rattail.db import model, Session as RattailSession
from rattail.db.continuum import model_transaction_query from rattail.db.continuum import model_transaction_query
@ -47,6 +48,7 @@ from rattail.csvutil import UnicodeDictWriter
from rattail.files import temp_path from rattail.files import temp_path
from rattail.excel import ExcelWriter from rattail.excel import ExcelWriter
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.util import OrderedDict
import colander import colander
import deform import deform
@ -124,6 +126,11 @@ class MasterView(View):
has_image = False has_image = False
has_thumbnail = False has_thumbnail = False
# can set this to true, and set type key as needed, and implement some
# other things also, to get a DB picker in the header for all views
supports_multiple_engines = False
engine_type_key = 'rattail'
row_attrs = {} row_attrs = {}
cell_attrs = {} cell_attrs = {}
@ -2096,8 +2103,9 @@ class MasterView(View):
""" """
if hasattr(cls, 'model_key'): if hasattr(cls, 'model_key'):
return cls.model_key return cls.model_key
mapper = orm.class_mapper(cls.get_model_class())
return ','.join([k.key for k in mapper.primary_key]) pkeys = get_primary_keys(cls.get_model_class())
return ','.join(pkeys)
@classmethod @classmethod
def get_model_title(cls): def get_model_title(cls):
@ -2323,12 +2331,50 @@ class MasterView(View):
return ['/mobile/master/{}.mako'.format(template)] return ['/mobile/master/{}.mako'.format(template)]
return ['/master/{}.mako'.format(template)] return ['/master/{}.mako'.format(template)]
def get_current_engine_dbkey(self):
"""
Returns the "current" engine's dbkey, for the current user.
"""
return self.request.session.get('tailbone.engines.{}.current'.format(self.engine_type_key),
'default')
def template_kwargs(self, **kwargs): def template_kwargs(self, **kwargs):
""" """
Supplement the template context, for all views. Supplement the template context, for all views.
""" """
# whether or not to show the DB picker?
kwargs['expose_db_picker'] = False
if self.supports_multiple_engines:
# view declares support for multiple engines, but we only want to
# show the picker if we have more than one engine configured
engines = self.get_db_engines()
if len(engines) > 1:
# user session determines "current" db engine *of this type*
# (note that many master views may declare the same type, and
# would therefore share the "current" engine)
selected = self.get_current_engine_dbkey()
kwargs['expose_db_picker'] = True
kwargs['db_picker_options'] = [tags.Option(k) for k in engines]
kwargs['db_picker_selected'] = selected
return kwargs return kwargs
def get_db_engines(self):
"""
Must return a dict (or even better, OrderedDict) which contains all
supported database engines for the master view. Used with the DB
picker feature.
"""
engines = OrderedDict()
if self.rattail_config.rattail_engine:
engines['default'] = self.rattail_config.rattail_engine
for dbkey in sorted(self.rattail_config.rattail_engines):
if dbkey != 'default':
engines[dbkey] = self.rattail_config.rattail_engines[dbkey]
return engines
############################## ##############################
# Grid Stuff # Grid Stuff
############################## ##############################
@ -2446,7 +2492,8 @@ class MasterView(View):
except orm.exc.UnmappedInstanceError: except orm.exc.UnmappedInstanceError:
return {self.model_key: row[self.model_key]} return {self.model_key: row[self.model_key]}
else: else:
keys = [k.key for k in mapper.primary_key] pkeys = get_primary_keys(row)
keys = list(pkeys)
values = [getattr(row, k) for k in keys] values = [getattr(row, k) for k in keys]
return dict(zip(keys, values)) return dict(zip(keys, values))