diff --git a/tailbone/templates/themes/falafel/base.mako b/tailbone/templates/themes/falafel/base.mako
index 15aad18b..27308697 100644
--- a/tailbone/templates/themes/falafel/base.mako
+++ b/tailbone/templates/themes/falafel/base.mako
@@ -132,6 +132,21 @@
% endif
+ % if expose_db_picker is not Undefined and expose_db_picker:
+
+
+ ${h.form(url('change_db_engine'))}
+ ${h.csrf_token(request)}
+ ${h.hidden('engine_type', value=master.engine_type_key)}
+
+ ${h.select('dbkey', db_picker_selected, db_picker_options, id='db-picker')}
+
+ ${h.end_form()}
+
+ % endif
+
@@ -303,6 +318,13 @@
var session_timeout = ${request.get_session_timeout() or 'null'};
var logout_url = '${request.route_url('logout')}';
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'):
$(function() {
$('#theme-picker').change(function() {
diff --git a/tailbone/views/common.py b/tailbone/views/common.py
index 8a5d560c..ba81babe 100644
--- a/tailbone/views/common.py
+++ b/tailbone/views/common.py
@@ -143,6 +143,20 @@ class CommonView(View):
self.request.session.flash("App theme has been changed to: {}".format(theme))
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):
"""
Generic view to handle the user feedback form.
@@ -212,6 +226,12 @@ class CommonView(View):
config.add_route('mobile.about', '/mobile/about')
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
config.add_tailbone_permission('common', 'common.change_app_theme',
"Change global App Template Theme")
diff --git a/tailbone/views/master.py b/tailbone/views/master.py
index 0c0cc29d..dd43b44f 100644
--- a/tailbone/views/master.py
+++ b/tailbone/views/master.py
@@ -36,6 +36,7 @@ import sqlalchemy as sa
from sqlalchemy import orm
import sqlalchemy_continuum as continuum
+from sqlalchemy_utils.functions import get_primary_keys
from rattail.db import model, Session as RattailSession
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.excel import ExcelWriter
from rattail.gpc import GPC
+from rattail.util import OrderedDict
import colander
import deform
@@ -124,6 +126,11 @@ class MasterView(View):
has_image = 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 = {}
cell_attrs = {}
@@ -2096,8 +2103,9 @@ class MasterView(View):
"""
if hasattr(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
def get_model_title(cls):
@@ -2323,12 +2331,50 @@ class MasterView(View):
return ['/mobile/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):
"""
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
+ 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
##############################
@@ -2446,7 +2492,8 @@ class MasterView(View):
except orm.exc.UnmappedInstanceError:
return {self.model_key: row[self.model_key]}
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]
return dict(zip(keys, values))