Add a field to the Server Dialogue allowing users to specify a subset of databases they'd like to see in the treeview. Fixes #1918

This commit is contained in:
Atul Sharma 2017-07-21 12:44:57 +01:00 committed by Dave Page
parent acaa79cf6b
commit 70418144cf
14 changed files with 130 additions and 18 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -45,3 +45,4 @@ Click the *Advanced* tab to continue.
Use the fields in the *Advanced* tab to configure a connection:
* Specify the IP address of the server host. Using this field to specify the host IP address will avoid a DNS lookup on connection, however it may be useful to specify both a host name and address when using Kerberos, GSSAPI, or SSPI authentication methods, as well as for verify-full SSL certificate verification
* The DB restriction field allows you to enter an SQL restriction that will be used against the pg_database table to limit the databases that you see. For example, you might enter: *live_db test_db* so that only live_db and test_db are shown in the pgAdmin browser. Separate entries with a comma or tab as you type.

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: d85a62333272
Revises: 3c1e4b6eda55
Create Date: 2017-07-07 16:03:23.842734
"""
from alembic import op
import sqlalchemy as sa
from pgadmin.model import db
# revision identifiers, used by Alembic.
revision = 'd85a62333272'
down_revision = 'f195f9a4923d'
branch_labels = None
depends_on = None
def upgrade():
db.engine.execute(
'ALTER TABLE server ADD COLUMN db_res TEXT'
)
def downgrade():
pass

View File

@ -380,7 +380,8 @@ class ServerNode(PGChildNodeView):
'sslmode': 'ssl_mode',
'gid': 'servergroup_id',
'comment': 'comment',
'role': 'role'
'role': 'role',
'db_res': 'db_res'
}
disp_lbl = {
@ -398,6 +399,8 @@ class ServerNode(PGChildNodeView):
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
if 'db_res' in data:
data['db_res'] = ','.join(data['db_res'])
if 'hostaddr' in data and data['hostaddr'] != '':
if not self.pat4.match(data['hostaddr']):
@ -497,7 +500,8 @@ class ServerNode(PGChildNodeView):
'role': server.role,
'connected': connected,
'version': manager.ver,
'server_type': manager.server_type if connected else 'pg'
'server_type': manager.server_type if connected else 'pg',
'db_res': server.db_res.split(',') if server.db_res else None
})
return ajax_response(
@ -544,7 +548,8 @@ class ServerNode(PGChildNodeView):
'connected': connected,
'version': manager.ver,
'sslmode': server.ssl_mode,
'server_type': manager.server_type if connected else 'pg'
'server_type': manager.server_type if connected else 'pg',
'db_res': server.db_res.split(',') if server.db_res else None
}
)
@ -597,7 +602,8 @@ class ServerNode(PGChildNodeView):
username=data[u'username'],
ssl_mode=data[u'sslmode'],
comment=data[u'comment'] if u'comment' in data else None,
role=data[u'role'] if u'role' in data else None
role=data[u'role'] if u'role' in data else None,
db_res=','.join(data[u'db_res']) if u'db_res' in data else None
)
db.session.add(server)
db.session.commit()

View File

@ -28,7 +28,9 @@ from pgadmin.utils.ajax import gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.browser.server_groups.servers import ServerNode
from flask_security import current_user
from pgadmin.model import Server
class DatabaseModule(CollectionNodeModule):
NODE_TYPE = 'database'
@ -166,11 +168,18 @@ class DatabaseView(PGChildNodeView):
(self.manager.db_info[self.manager.did])['datlastsysoid'] \
if self.manager.db_info is not None and \
self.manager.did in self.manager.db_info else 0
db_disp_res = None
params = None
if self.manager and self.manager.db_res:
db_disp_res = ", ".join(['%s'] * len(self.manager.db_res.split(',')))
params = tuple(self.manager.db_res.split(','))
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
conn=self.conn, last_system_oid=last_system_oid
conn=self.conn, last_system_oid=last_system_oid, db_restrictions=db_disp_res
)
status, res = self.conn.execute_dict(SQL)
status, res = self.conn.execute_dict(SQL, params)
if not status:
return internal_server_error(errormsg=res)
@ -188,12 +197,19 @@ class DatabaseView(PGChildNodeView):
if self.manager.db_info is not None and \
self.manager.did in self.manager.db_info else 0
)
server_node_res = self.manager
db_disp_res = None
params = None
if server_node_res and server_node_res.db_res:
db_disp_res = ", ".join(['%s']*len(server_node_res.db_res.split(',')))
params = tuple(server_node_res.db_res.split(','))
SQL = render_template(
"/".join([self.template_path, 'nodes.sql']),
last_system_oid=last_system_oid
last_system_oid=last_system_oid,
db_restrictions=db_disp_res
)
status, rset = self.conn.execute_dict(SQL)
status, rset = self.conn.execute_dict(SQL, params)
if not status:
return internal_server_error(errormsg=rset)
@ -851,12 +867,17 @@ class DatabaseView(PGChildNodeView):
if self.manager.db_info is not None and \
self.manager.did in self.manager.db_info else 0
db_disp_res = None
params = None
if self.manager and self.manager.db_res:
db_disp_res = ", ".join(['%s'] * len(self.manager.db_res.split(',')))
params = tuple(self.manager.db_res.split(','))
conn = self.manager.connection()
status, res = conn.execute_dict(
render_template(
status, res = conn.execute_dict(render_template(
"/".join([self.template_path, 'stats.sql']),
did=did, conn=conn, last_system_oid=last_system_oid
)
did=did, conn=conn, last_system_oid=last_system_oid, db_restrictions=db_disp_res
),params
)
if not status:

View File

@ -41,5 +41,10 @@ db.oid = {{ did|qtLiteral }}::OID{% else %}{% if name %}
db.datname = {{ name|qtLiteral }}::text{% else %}
db.oid > {{ last_system_oid|qtLiteral }}::OID
{% endif %}{% endif %}
{% if db_restrictions %}
AND
db.datname in ({{db_restrictions}})
{% endif %}
ORDER BY datname;

View File

@ -29,5 +29,10 @@ WHERE {% if did %}
db.datid = {{ did|qtLiteral }}::OID{% else %}
db.datid > {{ last_system_oid|qtLiteral }}::OID
{% endif %}
{% if db_restrictions %}
AND
db.datname in ({{db_restrictions}})
{% endif %}
ORDER BY db.datname;

View File

@ -8,5 +8,10 @@ WHERE {% if did %}
db.oid = {{ did|qtLiteral }}::OID{% else %}
db.oid > {{ last_system_oid }}::OID
{% endif %}
{% if db_restrictions %}
AND
db.datname in ({{db_restrictions}})
{% endif %}
ORDER BY datname;

View File

@ -34,5 +34,10 @@ db.oid = {{ did|qtLiteral }}::OID{% else %}{% if name %}
db.datname = {{ name|qtLiteral }}::text{% else %}
db.oid > {{ last_system_oid|qtLiteral }}::OID
{% endif %}{% endif %}
{% if db_restrictions %}
AND
db.datname in ({{db_restrictions}})
{% endif %}
ORDER BY datname;

View File

@ -24,5 +24,10 @@ WHERE {% if did %}
db.datid = {{ did|qtLiteral }}::OID{% else %}
db.datid > {{ last_system_oid|qtLiteral }}::OID
{% endif %}
{% if db_restrictions %}
AND
db.datname in ({{db_restrictions}})
{% endif %}
ORDER BY db.datname;

View File

@ -624,7 +624,8 @@ define('pgadmin.node.server', [
role: null,
connect_now: true,
password: undefined,
save_password: false
save_password: false,
db_res: ''
},
// Default values!
initialize: function(attrs, args) {
@ -669,6 +670,10 @@ define('pgadmin.node.server', [
},{
id: 'hostaddr', label: gettext('Host address'), type: 'text', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'db_res', label: gettext('DB restriction'), type: 'select2', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], disabled: 'isConnected', select2: {multiple: true, allowClear: false,
tags: true, tokenSeparators: [','], first_empty: false, selectOnClose: true, emptyOptions: true}
},{
id: 'port', label: gettext('Port'), type: 'int', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'], disabled: 'isConnected', min: 1024, max: 65535

View File

@ -128,6 +128,8 @@ class Server(db.Model):
servers = db.relationship('ServerGroup',
backref=db.backref('server', cascade="all, delete-orphan"),
lazy='joined')
db_res = db.Column(db.Text(), nullable=True)

View File

@ -1707,7 +1707,8 @@
defaults: _.extend({}, Backform.SelectControl.prototype.defaults, {
select2: {
first_empty: true,
multiple: false
multiple: false,
emptyOptions: false
}
}),
formatter: Select2Formatter,
@ -1761,7 +1762,8 @@
data.select2 = data.select2 || {};
_.defaults(data.select2, this.defaults.select2, {
first_empty: true,
multiple: false
multiple: false,
emptyOptions: false
});
// Evaluate the disabled, visible, and required option
@ -1807,8 +1809,29 @@
* Add empty option as Select2 requires any empty '<option><option>' for
* some of its functionality to work and initialize select2 control.
*/
if (data.select2.tags && data.select2.emptyOptions) {
select2Opts.data = data.rawValue;
}
this.$sel = this.$el.find("select").select2(select2Opts);
// Add or remove tags from select2 control
if (data.select2.tags && data.select2.emptyOptions) {
this.$sel.val(data.rawValue);
this.$sel.trigger('change.select2');
this.$sel.on('select2:unselect', function(evt) {
$(this).find('option[value="'+evt.params.data.text.replace("'","\\'").replace('"','\\"')+'"]').remove();
$(this).trigger('change.select2');
if ($(this).val() == null) {
$(this).empty();
}
});
}
// Select the highlighted item on Tab press.
if (this.$sel) {
this.$sel.data('select2').on("keypress", function(ev) {

View File

@ -1615,6 +1615,7 @@ class ServerManager(object):
self.pinged = datetime.datetime.now()
self.db_info = dict()
self.server_types = None
self.db_res = server.db_res
for con in self.connections:
self.connections[con]._release()