JSON Rule Engine
A powerful and lightweight Python library for building, evaluating, and translating JSON-based business rules. Perfect for creating dynamic filtering systems, decision engines, and converting rules to Django ORM queries.
Key Features
- 🎯 Unified API: Single
RuleEngineclass for all functionality - 🔧 Configurable: No hardcoded domain assumptions - works for any use case
- 🐍 Pythonic: Intuitive
Field()andQ()builders for rule creation - ⚡ Zero Dependencies: Core functionality requires no external packages
- 🔌 Django Integration: Optional Django Q object conversion
- 📊 Extensible: Add custom operators and configurations
Installation
# Basic installation
pip install json-rule-engine
# With Django support
pip install json-rule-engine[django]
Quick Start
Initialize the Engine
from json_rule_engine import RuleEngine, Field, Q
# Create the engine - single entry point for all functionality
engine = RuleEngine()
Build Rules
# Using Field builder (recommended)
rule = Field('age').greater_than(18)
rule = Field('city').equals('NYC')
rule = Field('email').contains('@gmail.com')
# Combine rules with & (AND), | (OR), ~ (NOT)
rule = (
Field('city').equals('NYC') &
Field('age').gt(18) &
Field('tags').has_any(['premium', 'vip'])
)
# Using Django-style Q objects
rule = Q(city='NYC') & Q(age__gt=18)
Evaluate Rules
# Evaluate against dictionary
data = {'city': 'NYC', 'age': 25, 'tags': ['vip']}
result = engine.evaluate(rule, data) # True
# Check matches (returns bool)
matches = engine.matches(rule, data) # True
Django Integration
# Convert to Django Q object (requires Django)
q = engine.to_q(rule)
# Use in Django ORM
from myapp.models import Contact
contacts = Contact.objects.filter(q)
Advanced Usage
Custom Configuration
The engine is fully configurable - no hardcoded assumptions:
from json_rule_engine import RuleEngine, DependencyConfig, RuleFields
# Configure for your domain (e-commerce example)
config = DependencyConfig(
id_fields={
'categories': 'category_ids',
'products': 'product_ids',
'vendors': 'vendor_ids',
},
custom_field_pattern=r'^custom\.(\d+)\.(\w+)$'
)
# Initialize engine with custom config
engine = RuleEngine(dependency_config=config)
# Now your fields are recognized
rule = Field('categories').has_any([101, 102])
deps = engine.get_dependencies(rule) # Returns RuleFields
print(deps.id_references['category_ids']) # {101, 102}
Working with Objects
from json_rule_engine import RuleEntity
class Customer(RuleEntity):
def __init__(self, name, age, city, tags=None):
self.name = name
self.age = age
self.city = city
self.tags = tags or []
def to_eval_dict(self):
return {
'name': self.name,
'age': self.age,
'city': self.city,
'tags': self.tags
}
# Create customers
customers = [
Customer('Alice', 25, 'NYC', ['vip']),
Customer('Bob', 17, 'LA', ['regular']),
Customer('Charlie', 30, 'NYC', ['premium'])
]
# Define rule
rule = Field('city').equals('NYC') & Field('age').gte(18)
# Batch evaluation
results = engine.batch(rule, customers)
print(f"Matches: {results['matches']}") # [Alice, Charlie]
print(f"Non-matches: {results['non_matches']}") # [Bob]
# Filter matching objects
nyc_adults = engine.filter(rule, customers) # [Alice, Charlie]
# Test with timing
result = engine.test(rule, customers[0]) # Returns EvaluationResult
print(f"Match: {result.matches}, Time: {result.eval_time_ms}ms")
Custom Operators
# Register custom operators
def between(values, data):
"""Check if value is between min and max."""
val, min_val, max_val = values
return min_val <= val <= max_val
engine.register_operator('between', between)
# Use custom operator
rule = {"between": [{"var": "age"}, 18, 65]}
result = engine.evaluate(rule, {"age": 25}) # True
Complex Nested Rules
# Build complex business logic
rule = RuleBuilder.and_(
RuleBuilder.field('status').equals('active'),
RuleBuilder.or_(
RuleBuilder.field('role').equals('admin'),
RuleBuilder.and_(
RuleBuilder.field('role').equals('user'),
RuleBuilder.field('permissions').has_any(['write', 'delete'])
)
),
RuleBuilder.not_(
RuleBuilder.field('banned').equals(True)
)
)
# Evaluate complex rule
data = {
'status': 'active',
'role': 'user',
'permissions': ['read', 'write'],
'banned': False
}
result = engine.evaluate(rule, data) # True
Working with JSON Rules
# Parse existing JSON rules
json_rule = {
"and": [
{"==": [{"var": "status"}, "active"]},
{"or": [
{">": [{"var": "credits"}, 100]},
{"==": [{"var": "plan"}, "premium"]}
]}
]
}
# Evaluate JSON directly
result = engine.evaluate(json_rule, data)
# Or wrap and combine with builder
from json_rule_engine import JsonRule
wrapped = JsonRule(json_rule)
combined = wrapped & Field('region').equals('US')
API Reference
RuleEngine Methods
The RuleEngine class is the main API entry point:
engine = RuleEngine(
dependency_config=None, # Optional: Custom dependency configuration
django_field_map=None # Optional: Custom Django field mappings
)
# Rule Evaluation
engine.evaluate(rule, data) # Evaluate rule against data
engine.matches(rule, data) # Returns bool
engine.test(rule, obj) # Test RuleEntity object with timing
engine.batch(rule, objects) # Evaluate multiple objects
engine.filter(rule, objects) # Filter matching objects
# Django Integration
engine.to_q(rule) # Convert to Django Q object
engine.to_q_with_explanation(rule) # Get Q object with explanation
engine.validate_json_rules(json) # Validate JSON structure
# Dependency Extraction
engine.get_dependencies(rule) # Extract field dependencies → RuleFields
# Configuration
engine.register_operator(name, func) # Add custom operator
engine.configure_dependencies(config) # Update dependency config
engine.configure_django_fields(map) # Update Django mappings
Rule Builders
# Field builder (recommended)
Field('name').equals(value)
Field('name').not_equals(value)
Field('name').greater_than(value)
Field('name').greater_or_equal(value)
Field('name').less_than(value)
Field('name').less_or_equal(value)
Field('name').contains(substring)
Field('name').startswith(prefix)
Field('name').endswith(suffix)
Field('name').is_empty()
Field('name').is_not_empty()
Field('name').has_any([values]) # For array fields
Field('name').has_all([values]) # Must have all
Field('name').has_none([values]) # Must have none
# Q objects (Django-style)
Q(field='value') # equals
Q(field__ne='value') # not equals
Q(field__gt=10) # greater than
Q(field__gte=10) # greater or equal
Q(field__lt=10) # less than
Q(field__lte=10) # less or equal
Q(field__contains='text') # contains
Q(field__has_any=[1,2]) # array operations
# Combining rules
rule1 & rule2 # AND
rule1 | rule2 # OR
~rule # NOT
# Helper functions
AND(rule1, rule2, rule3)
OR(rule1, rule2)
NOT(rule)
Supported Operators
Comparison
==(equals)!=(not equals)>(greater than)>=(greater or equal)<(less than)<=(less or equal)
String
contains- Substring checkstartswith- String prefixendswith- String suffixis_empty- Null or empty stringis_not_empty- Has value
Array/Set
has_any- Has at least one valuehas_all- Has all valueshas_none- Has none of the valuesis_in- Value is in list
Logic
&/AND- Logical AND|/OR- Logical OR~/NOT- Logical NOT
Migration from v1.x
Version 2.0 introduces a cleaner, unified API:
Old API (v1.x)
# Multiple confusing entry points
from json_rule_engine import evaluate, matches, to_q, json_to_q
result = evaluate(rule, data)
q = to_q(rule)
q = json_to_q(json_rules)
New API (v2.0)
# Single, clear entry point
from json_rule_engine import RuleEngine
engine = RuleEngine()
result = engine.evaluate(rule, data)
q = engine.to_q(rule)
Key Changes
- Unified API: All functionality through
RuleEngineclass - Configurable Dependencies: No more hardcoded field assumptions
- Professional Class Names:
Evaluatable→RuleEntityDependencies→RuleFieldsEvalResult→EvaluationResult
- Cleaner Imports: Single entry point for discoverability
- Better Typing: Full type hints throughout
Examples
E-commerce Product Filtering
# Configure for e-commerce domain
config = DependencyConfig(
id_fields={
'categories': 'category_ids',
'brands': 'brand_ids',
'tags': 'tag_ids'
}
)
engine = RuleEngine(dependency_config=config)
# Build product filter rule
rule = (
Field('price').between(10, 100) &
Field('categories').has_any(['electronics', 'computers']) &
Field('in_stock').equals(True) &
(Field('rating').gte(4) | Field('featured').equals(True))
)
# Filter products
matching_products = engine.filter(rule, all_products)
User Permission System
# Define permission rule
can_edit = (
Field('role').equals('admin') |
(
Field('role').equals('editor') &
Field('departments').has_any(['content', 'marketing'])
)
)
# Check user permission
user_data = {
'role': 'editor',
'departments': ['content', 'design']
}
has_permission = engine.evaluate(can_edit, user_data) # True
Dynamic Form Validation
# Build validation rule from configuration
validation_rule = RuleBuilder.and_(
RuleBuilder.field('age').gte(18),
RuleBuilder.field('email').contains('@'),
RuleBuilder.or_(
RuleBuilder.field('phone').is_not_empty(),
RuleBuilder.field('email').is_not_empty()
)
)
# Validate form submission
form_data = {
'age': 25,
'email': 'user@example.com',
'phone': ''
}
is_valid = engine.matches(validation_rule, form_data) # True
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you encounter any problems or have questions, please open an issue on GitHub.
Metadata
Release files for json-rule-engine 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| json_rule_engine-2.1.0.tar.gz | 32.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| json_rule_engine-2.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 56.1 kB
Release files / json_rule_engine-2.1.0.tar.gz
| Download URL | json_rule_engine-2.1.0.tar.gz |
|---|---|
| Size | 32.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba32ec285f76cbcb4dc78b38eaaa74536f3da37a8979caed97a5d9c37277245a
|
|
BLAKE2b-256 checksum How to use checksums |
6c7f4a5826680cda0c560681e2fc47b2e548bf5e4160320408e76a190ae24d51
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Release files / json_rule_engine-2.1.0-py3-none-any.whl
| Download URL | json_rule_engine-2.1.0-py3-none-any.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b73afd6b436d7a420dfd83ca254fde616231c846d2f4d1b77ac0abb495d18e4e
|
|
BLAKE2b-256 checksum How to use checksums |
713acda20a2d98866b0f046c3f8ff23f6ad7ea709054a006d9e4dd26d6834028
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|