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

Comments:

Patch:
diff --git a/.travis.yml b/.travis.yml
index ec8e9e5..5763d29 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,10 @@
 language: python
 python:
-- '3.4'
 - '3.5'
 - '3.6'
 - '3.7'
+- '3.8'
+- '3.9'
 install:
 - pip install -r requirements.txt
 - pip install coveralls
diff --git a/README.md b/README.md
index 37fb3e8..56ea6ed 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 SQLAlchemy Adapter for PyCasbin 
 ====
 
-[![Build Status](https://www.travis-ci.org/pycasbin/sqlalchemy-adapter.svg?branch=master)](https://www.travis-ci.org/pycasbin/sqlalchemy-adapter)
+[![Build Status](https://www.travis-ci.com/pycasbin/sqlalchemy-adapter.svg?branch=master)](https://www.travis-ci.com/pycasbin/sqlalchemy-adapter)
 [![Coverage Status](https://coveralls.io/repos/github/pycasbin/sqlalchemy-adapter/badge.svg)](https://coveralls.io/github/pycasbin/sqlalchemy-adapter)
 [![Version](https://img.shields.io/pypi/v/casbin_sqlalchemy_adapter.svg)](https://pypi.org/project/casbin_sqlalchemy_adapter/)
 [![PyPI - Wheel](https://img.shields.io/pypi/wheel/casbin_sqlalchemy_adapter.svg)](https://pypi.org/project/casbin_sqlalchemy_adapter/)
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'))