Added multiple drop/delete functionality for the table constraints. Fixes #3900

pull/32/head
Khushboo Vashi 2020-04-22 15:45:48 +05:30 committed by Akshay Joshi
parent 553eeb034d
commit 6e5dbf7beb
9 changed files with 206 additions and 3 deletions

View File

@ -32,6 +32,7 @@ Bug fixes
| `Issue #3495 <https://redmine.postgresql.org/issues/3495>`_ - Fixed an issue where the query tool unable to load the file which contains the BOM marker.
| `Issue #3523 <https://redmine.postgresql.org/issues/3523>`_ - Fixed an issue where right-clicking a browser object does not apply to the object on which right-click was fired.
| `Issue #3645 <https://redmine.postgresql.org/issues/3645>`_ - Ensure that the start and end date should be deleted when clear the selection for pgAgent Job.
| `Issue #3900 <https://redmine.postgresql.org/issues/3900>`_ - Added multiple drop/delete functionality for the table constraints.
| `Issue #3947 <https://redmine.postgresql.org/issues/3947>`_ - Fixed copy-paste row issues in View/Edit Data.
| `Issue #3972 <https://redmine.postgresql.org/issues/3972>`_ - Modified keyboard shortcuts in Query Tool for OSX native support.
| `Issue #3988 <https://redmine.postgresql.org/issues/3988>`_ - Fixed cursor disappeared issue in the query editor for some of the characters when zoomed out.

View File

@ -9,6 +9,8 @@
"""Implements Constraint Node"""
import json
from flask import request
from functools import wraps
from pgadmin.utils.driver import get_driver
import pgadmin.browser.server_groups.servers.databases as database
@ -132,3 +134,48 @@ def proplist(**kwargs):
response=res,
status=200
)
@blueprint.route('/obj/<int:gid>/<int:sid>/<int:did>/<int:scid>/<int:tid>/',
methods=['DELETE'])
@blueprint.route('/delete/<int:gid>/<int:sid>/<int:did>/<int:scid>/<int:tid>/',
methods=['DELETE'])
def delete(**kwargs):
"""
Delete multiple constraints under the table.
Args:
**kwargs:
Returns:
"""
data = request.form if request.form else json.loads(
request.data, encoding='utf-8')
if 'delete' in request.base_url:
cmd = {"cmd": "delete"}
else:
cmd = {"cmd": "obj"}
res = []
module_wise_data = {}
for d in data['ids']:
if d['_type'] in module_wise_data:
module_wise_data[d['_type']].append(d['id'])
else:
module_wise_data[d['_type']] = [d['id']]
for name in ConstraintRegistry.registry:
if name in module_wise_data:
module = (ConstraintRegistry.registry[name])['nodeview']
view = module(**cmd)
request.data = json.dumps({'ids': module_wise_data[name]})
response = view.delete(**kwargs)
res = json.loads(response.data.decode('utf-8'))
if not res['success']:
return response
return make_json_response(
success=1,
info=gettext("Constraints dropped.")
)

View File

@ -272,6 +272,9 @@ class CheckConstraintView(PGChildNodeView):
tid=tid)
status, res = self.conn.execute_dict(SQL)
for row in res['rows']:
row['_type'] = self.node_type
return res['rows']
@check_precondition

View File

@ -339,6 +339,9 @@ class ExclusionConstraintView(PGChildNodeView):
tid=tid)
status, res = self.conn.execute_dict(SQL)
for row in res['rows']:
row['_type'] = self.node_type
return res['rows']
@check_precondition

View File

@ -349,6 +349,9 @@ class ForeignKeyConstraintView(PGChildNodeView):
tid=tid)
status, res = self.conn.execute_dict(SQL)
for row in res['rows']:
row['_type'] = self.node_type
return res['rows']
@check_precondition

View File

@ -359,6 +359,9 @@ class IndexConstraintView(PGChildNodeView):
constraint_type=self.constraint_type)
status, res = self.conn.execute_dict(SQL)
for row in res['rows']:
row['_type'] = self.node_type
return res['rows']
@check_precondition

View File

@ -22,8 +22,8 @@ define('pgadmin.node.constraints', [
label: gettext('Constraints'),
type: 'coll-constraints',
columns: ['name', 'comment'],
canDrop: false,
canDropCascade: false,
canDrop: true,
canDropCascade: true,
});
}

View File

@ -0,0 +1,137 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2020, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import json
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.utils.route import BaseTestGenerator
from regression import parent_node_dict
from regression.python_test_utils import test_utils as utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
import utils as tables_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.check_constraint.tests import utils as chk_constraint_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.exclusion_constraint.tests import utils as exclusion_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.foreign_key.tests import utils as fk_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.index_constraint.tests import utils as index_constraint_utils
class ConstraintDeleteMultipleTestCase(BaseTestGenerator):
"""This class will delete constraints under table node."""
scenarios = [
# Fetching default URL for table node.
('Delete Constraints', dict(url='/browser/constraints/obj/'))
]
def setUp(self):
self.db_name = parent_node_dict["database"][-1]["db_name"]
schema_info = parent_node_dict["schema"][-1]
self.server_id = schema_info["server_id"]
self.db_id = schema_info["db_id"]
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
self.server_id, self.db_id)
if not db_con['data']["connected"]:
raise Exception("Could not connect to database to add a table.")
self.schema_id = schema_info["schema_id"]
self.schema_name = schema_info["schema_name"]
schema_response = schema_utils.verify_schemas(self.server,
self.db_name,
self.schema_name)
if not schema_response:
raise Exception("Could not find the schema to add a table.")
self.table_name = "table_constraint_delete_%s" % \
(str(uuid.uuid4())[1:8])
self.table_id = tables_utils.create_table(self.server,
self.db_name,
self.schema_name,
self.table_name)
# Create Check Constraints
self.check_constraint_name = "test_constraint_delete_%s" % \
(str(uuid.uuid4())[1:8])
self.check_constraint_id = \
chk_constraint_utils.create_check_constraint(
self.server, self.db_name, self.schema_name, self.table_name,
self.check_constraint_name)
self.check_constraint_name_1 = "test_constraint_delete1_%s" % (
str(uuid.uuid4())[1:8])
self.check_constraint_id_1 = \
chk_constraint_utils.create_check_constraint(
self.server, self.db_name, self.schema_name, self.table_name,
self.check_constraint_name_1)
# Create Exclusion Constraint
self.exclustion_constraint_name = "test_exclusion_get_%s" % (
str(uuid.uuid4())[1:8])
self.exclustion_constraint_id = \
exclusion_utils.create_exclusion_constraint(
self.server, self.db_name, self.schema_name, self.table_name,
self.exclustion_constraint_name
)
# Create Foreign Key
self.foreign_table_name = "foreign_table_foreignkey_get_%s" % \
(str(uuid.uuid4())[1:8])
self.foreign_table_id = tables_utils.create_table(
self.server, self.db_name, self.schema_name,
self.foreign_table_name)
self.foreign_key_name = "test_foreignkey_get_%s" % \
(str(uuid.uuid4())[1:8])
self.foreign_key_id = fk_utils.create_foreignkey(
self.server, self.db_name, self.schema_name, self.table_name,
self.foreign_table_name)
# Create Primary Key
self.primary_key_name = "test_primary_key_get_%s" % \
(str(uuid.uuid4())[1:8])
self.primary_key_id = \
index_constraint_utils.create_index_constraint(
self.server, self.db_name, self.schema_name, self.table_name,
self.primary_key_name, "PRIMARY KEY")
# Create Unique Key constraint
self.unique_constraint_name = "test_unique_constraint_get_%s" % (
str(uuid.uuid4())[1:8])
self.unique_constraint_id = \
index_constraint_utils.create_index_constraint(
self.server, self.db_name, self.schema_name, self.table_name,
self.unique_constraint_name, "UNIQUE")
def runTest(self):
"""This function will delete constraints under table node."""
data = {'ids': [
{'id': self.check_constraint_id, '_type': 'check_constraint'},
{'id': self.check_constraint_id_1, '_type': 'check_constraint'},
{'id': self.exclustion_constraint_id,
'_type': 'exclustion_constraint'},
{'id': self.foreign_key_id, '_type': 'foreign_key'},
{'id': self.primary_key_id, '_type': 'index_constraint'},
{'id': self.unique_constraint_id, '_type': 'index_constraint'}
]}
response = self.tester.delete(self.url + str(utils.SERVER_GROUP) +
'/' + str(self.server_id) + '/' +
str(self.db_id) + '/' +
str(self.schema_id) + '/' +
str(self.table_id) + '/',
data=json.dumps(data),
content_type='html/json',
follow_redirects=True)
self.assertEquals(response.status_code, 200)
def tearDown(self):
# Disconnect the database
database_utils.disconnect_database(self, self.server_id, self.db_id)

View File

@ -354,7 +354,13 @@ define([
msg = undefined,
title = undefined;
_.each(sel_row_models, function(r){ sel_rows.push(r.id); });
if (node.type && node.type == 'coll-constraints') {
// In order to identify the constraint type, the type should be passed to the server
sel_rows = sel_row_models.map(row => ({id: row.get('oid'), _type: row.get('_type')}));
}
else {
sel_rows = sel_row_models.map(row => row.id);
}
if (sel_rows.length === 0) {
Alertify.alert(gettext('Drop Multiple'),