Repository: pycasbin/sqlalchemy-adapter

Issue:
How should I use batch operations？ it seems that there is no “add_policies” method
What should I do if I implement it myself

Discussion:
hsluoyz: @wakemaster39 plz:

1. Fix the CI error.
2. Link your github account with your git committer, to show the avatar:

![image](https://user-images.githubusercontent.com/3787410/115117100-8696ab80-9fcf-11eb-911b-97aa14d3baad.png)

hsluoyz: @techoner @Zxilly plz review.

Patch:
diff --git a/casbin_sqlalchemy_adapter/adapter.py b/casbin_sqlalchemy_adapter/adapter.py
index 9f47816..d7b3881 100644
--- a/casbin_sqlalchemy_adapter/adapter.py
+++ b/casbin_sqlalchemy_adapter/adapter.py
@@ -123,6 +123,12 @@ class Adapter(persist.Adapter):
         self._save_policy_line(ptype, rule)
         self._commit()
 
+    def add_policies(self, sec, ptype, rules):
+        """adds a policy rules to the storage."""
+        for rule in rules:
+            self._save_policy_line(ptype, rule)
+        self._commit()
+
     def remove_policy(self, sec, ptype, rule):
         """removes a policy rule from the storage."""
         query = self._session.query(self._db_class)
@@ -134,6 +140,16 @@ class Adapter(persist.Adapter):
 
         return True if r > 0 else False
 
+    def remove_policies(self, sec, ptype, rules):
+        """removes a policy rules from the storage."""
+        query = self._session.query(self._db_class)
+        query = query.filter(self._db_class.ptype == ptype)
+        for rule in rules:
+            query = query.filter(or_(getattr(self._db_class, "v{}".format(i)) == v for i, v in enumerate(rule)))
+        query.delete()
+        self._commit()
+
+
     def remove_filtered_policy(self, sec, ptype, field_index, *field_values):
         """removes policy rules that match the filter from the storage.
         This is part of the Auto-Save feature.


Test patch:
diff --git a/tests/test_adapter.py b/tests/test_adapter.py
index a9ada02..5e1caeb 100644
--- a/tests/test_adapter.py
+++ b/tests/test_adapter.py
@@ -49,6 +49,15 @@ class TestConfig(TestCase):
     def test_add_policy(self):
         e = get_enforcer()
 
+        self.assertFalse(e.enforce('eve', 'data3', 'read'))
+        res = e.add_policies((('eve', 'data3', 'read'), ('eve', 'data4', 'read')))
+        self.assertTrue(res)
+        self.assertTrue(e.enforce('eve', 'data3', 'read'))
+        self.assertTrue(e.enforce('eve', 'data4', 'read'))
+
+    def test_add_policies(self):
+        e = get_enforcer()
+
         self.assertFalse(e.enforce('eve', 'data3', 'read'))
         res = e.add_permission_for_user('eve', 'data3', 'read')
         self.assertTrue(res)
@@ -76,6 +85,18 @@ class TestConfig(TestCase):
         e.delete_permission_for_user('alice', 'data5', 'read')
         self.assertFalse(e.enforce('alice', 'data5', 'read'))
 
+    def test_remove_policies(self):
+        e = get_enforcer()
+
+        self.assertFalse(e.enforce('alice', 'data5', 'read'))
+        self.assertFalse(e.enforce('alice', 'data6', 'read'))
+        e.add_policies((('alice', 'data5', 'read'), ('alice', 'data6', 'read')))
+        self.assertTrue(e.enforce('alice', 'data5', 'read'))
+        self.assertTrue(e.enforce('alice', 'data6', 'read'))
+        e.remove_policies((('alice', 'data5', 'read'), ('alice', 'data6', 'read')))
+        self.assertFalse(e.enforce('alice', 'data5', 'read'))
+        self.assertFalse(e.enforce('alice', 'data6', 'read'))
+
     def test_remove_filtered_policy(self):
         e = get_enforcer()
 
@@ -139,7 +160,7 @@ class TestConfig(TestCase):
     def test_filtered_policy(self):
         e= get_enforcer()
         filter = Filter()
-        
+
         filter.ptype = ['p']
         e.load_filtered_policy(filter)
         self.assertTrue(e.enforce('alice', 'data1', 'read'))
@@ -150,7 +171,7 @@ class TestConfig(TestCase):
         self.assertFalse(e.enforce('bob', 'data1', 'write'))
         self.assertFalse(e.enforce('bob', 'data2', 'read'))
         self.assertTrue(e.enforce('bob', 'data2', 'write'))
-        
+
         filter.ptype = []
         filter.v0 = ['alice']
         e.load_filtered_policy(filter)
@@ -164,7 +185,7 @@ class TestConfig(TestCase):
         self.assertFalse(e.enforce('bob', 'data2', 'write'))
         self.assertFalse(e.enforce('data2_admin', 'data2','read'))
         self.assertFalse(e.enforce('data2_admin', 'data2','write'))
-        
+
         filter.v0 = ['bob']
         e.load_filtered_policy(filter)
         self.assertFalse(e.enforce('alice', 'data1', 'read'))
@@ -177,7 +198,7 @@ class TestConfig(TestCase):
         self.assertTrue(e.enforce('bob', 'data2', 'write'))
         self.assertFalse(e.enforce('data2_admin', 'data2','read'))
         self.assertFalse(e.enforce('data2_admin', 'data2','write'))
-        
+
         filter.v0 = ['data2_admin']
         e.load_filtered_policy(filter)
         self.assertTrue(e.enforce('data2_admin', 'data2','read'))
@@ -203,7 +224,7 @@ class TestConfig(TestCase):
         self.assertTrue(e.enforce('bob', 'data2', 'write'))
         self.assertFalse(e.enforce('data2_admin', 'data2','read'))
         self.assertFalse(e.enforce('data2_admin', 'data2','write'))
-        
+
         filter.v0 = []
         filter.v1 = ['data1']
         e.load_filtered_policy(filter)
@@ -230,7 +251,7 @@ class TestConfig(TestCase):
         self.assertTrue(e.enforce('bob', 'data2', 'write'))
         self.assertTrue(e.enforce('data2_admin', 'data2','read'))
         self.assertTrue(e.enforce('data2_admin', 'data2','write'))
-        
+
         filter.v1 = []
         filter.v2 = ['read']
         e.load_filtered_policy(filter)
@@ -256,4 +277,4 @@ class TestConfig(TestCase):
         self.assertFalse(e.enforce('bob', 'data2', 'read'))
         self.assertTrue(e.enforce('bob', 'data2', 'write'))
         self.assertFalse(e.enforce('data2_admin', 'data2','read'))
-        self.assertTrue(e.enforce('data2_admin', 'data2','write'))
\ No newline at end of file
+        self.assertTrue(e.enforce('data2_admin', 'data2','write'))