SQLAlchemy Adapter for PyCasbin
SQLAlchemy Adapter is the SQLAlchemy adapter for PyCasbin. With this library, Casbin can load policy from SQLAlchemy supported database or save policy to it. It supports various databases like PostgreSQL, MySQL, SQLite, Oracle, Microsoft SQL Server, Firebird, and Sybase. The library is currently at version 1.4.0 and releases are frequent, often including new features and compatibility updates.
Common errors
-
ModuleNotFoundError: No module named 'casbin_sqlalchemy_adapter'
cause The package was either not installed, or there's a typo in the import statement.fixEnsure the package is installed using `pip install casbin-sqlalchemy-adapter`. Verify the import statement is `from casbin_sqlalchemy_adapter import Adapter`. -
TypeError: 'declarative_base' object is not callable or similar SQLAlchemy 2.0 migration errors.
cause Incompatibility between the adapter version and your SQLAlchemy version, specifically related to the `declarative_base` import style which changed in SQLAlchemy 2.0.fixUpgrade `casbin-sqlalchemy-adapter` to its latest version (`>=1.1.0`) which includes fixes for SQLAlchemy 2.0's `DeclarativeBase`. If still encountering issues, ensure your application's SQLAlchemy usage aligns with 2.0 conventions. -
Table 'casbin_rule' not found or Policy changes not persisting to database.
cause The `casbin_rule` table was not created, or policies are not being saved. This can happen if `create_table=False` was set, or if an older adapter version without auto-save is used and `e.save_policy()` is not called.fixEnsure `create_table` is `True` (default) or that your migration system creates the `casbin_rule` table. For programmatic policy changes, confirm that auto-save is enabled (default behavior) or explicitly call `e.save_policy()` after modifications if you are managing policies manually or with an older adapter/enforcer. -
Database deadlock or concurrency issues when using sessions.
cause Older versions of the adapter had issues with nested database sessions leading to deadlocks.fixUpgrade to `casbin-sqlalchemy-adapter` version 1.2.0 or newer, which includes a fix for nested session deadlocks.
Warnings
- breaking Version 1.0.0 introduced breaking changes, largely due to internal upgrades and potentially higher Python/dependency version requirements. While specific API changes weren't detailed in the changelog, a major version bump indicates non-backward compatible changes.
- gotcha The library has undergone updates to ensure compatibility with SQLAlchemy 2.0. Older applications using SQLAlchemy 1.x might face issues, especially with declarative base imports (e.g., `declarative_base` vs `DeclarativeBase`).
- gotcha By default, the adapter automatically creates the necessary `casbin_rule` database table during initialization. This might conflict with applications managing schema migrations (e.g., via Alembic).
- gotcha Soft deletion for Casbin rules is supported, but requires providing a custom Casbin rule model and explicitly passing the `db_class_softdelete_attribute` during adapter initialization.
Install
-
pip install casbin-sqlalchemy-adapter
Imports
- Adapter
from sqlalchemy_adapter import Adapter
from casbin_sqlalchemy_adapter import Adapter
Quickstart
import casbin
from casbin_sqlalchemy_adapter import Adapter
import os
# Define paths for your Casbin model and policy files
# For a real application, these would be proper paths to .conf and .csv files
# Example model.conf content:
# [request_definition]
# r = sub, obj, act
# [policy_definition]
# p = sub, obj, act
# [policy_effect]
# e = some(where (p.eft == allow))
# [matchers]
# m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
#
# Example policy.csv content:
# p, alice, data1, read
# p, bob, data2, write
model_conf_path = os.path.join(os.path.dirname(__file__), "model.conf")
policy_csv_path = os.path.join(os.path.dirname(__file__), "policy.csv")
# Create dummy model and policy files for demonstration if they don't exist
if not os.path.exists(model_conf_path):
with open(model_conf_path, 'w') as f:
f.write("""
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
""")
if not os.path.exists(policy_csv_path):
with open(policy_csv_path, 'w') as f:
f.write("""
p, alice, data1, read
p, bob, data2, write
""")
# Initialize the SQLAlchemy adapter with a SQLite database
# Replace 'sqlite:///test.db' with your database connection string (e.g., 'mysql+pymysql://user:password@host/dbname')
adapter = Adapter('sqlite:///test.db') #
# Initialize the Casbin Enforcer
# The enforcer will load the model from model.conf and policies from the adapter
e = casbin.Enforcer(model_conf_path, adapter) #
# Add a policy via the Enforcer (this will be saved to the database)
# Note: policies added directly via the Enforcer API are automatically saved if Auto-Save is enabled (default).
if e.add_policy("charlie", "data3", "edit"):
print("Policy 'charlie, data3, edit' added.")
# Enforce a policy
sub = "alice"
obj = "data1"
act = "read"
if e.enforce(sub, obj, act):
print(f"{sub} is permitted to {act} {obj}.")
else:
print(f"{sub} is denied to {act} {obj}.")
sub = "bob"
obj = "data1"
act = "read"
if e.enforce(sub, obj, act):
print(f"{sub} is permitted to {act} {obj}.")
else:
print(f"{sub} is denied to {act} {obj}.")
sub = "charlie"
obj = "data3"
act = "edit"
if e.enforce(sub, obj, act):
print(f"{sub} is permitted to {act} {obj}.")
else:
print(f"{sub} is denied to {act} {obj}.")
# Remove a policy
if e.remove_policy("charlie", "data3", "edit"):
print("Policy 'charlie, data3, edit' removed.")
# Check again after removal
if not e.enforce("charlie", "data3", "edit"):
print("Charlie is now denied to edit data3 as expected.")