# django-datalog
A logic programming and inference engine for Django applications.
## Installation
```bash
pip install django-datalog
```
```python
# settings.py
INSTALLED_APPS = ['django_datalog']
```
```bash
python manage.py migrate
```
## Core Concepts
### Facts
Define facts as Python classes with Django model integration:
```python
from django_datalog.models import Fact, Var
class WorksFor(Fact):
subject: Employee | Var # Employee
object: Company | Var # Company
class ColleaguesOf(Fact, inferred=True): # Inferred facts can't be stored directly
subject: Employee | Var
object: Employee | Var
```
### Rules
Define inference logic with tuples (AND) and lists (OR):
```python
from django_datalog.rules import rule
# Simple rule: Colleagues work at same company
rule(
ColleaguesOf(Var("emp1"), Var("emp2")),
WorksFor(Var("emp1"), Var("company")) & WorksFor(Var("emp2"), Var("company"))
)
# Disjunctive rule: HasAccess via admin OR manager
rule(
HasAccess(Var("user"), Var("resource")),
IsAdmin(Var("user")) | IsManager(Var("user"), Var("resource"))
)
# Mixed rule: Complex access control
rule(
CanEdit(Var("user"), Var("doc")),
IsOwner(Var("user"), Var("doc")) |
(IsManager(Var("user"), Var("folder")) & Contains(Var("folder"), Var("doc")))
)
```
### Fact Operators
Use `|` (OR) and `&` (AND) operators:
```python
# Modern operator syntax (recommended):
rule(head, fact1 | fact2) # OR: fact1 OR fact2
rule(head, fact1 & fact2) # AND: fact1 AND fact2
# Combining operators:
rule(head, (fact1 & fact2) | fact3) # (fact1 AND fact2) OR fact3
rule(head, fact1 & fact2 & fact3) # fact1 AND fact2 AND fact3
rule(head, fact1 | fact2 | fact3) # fact1 OR fact2 OR fact3
# Legacy syntax (still supported):
rule(head, [fact1, fact2]) # OR (list syntax)
rule(head, (fact1, fact2)) # AND (tuple syntax)
```
### Storing Facts
```python
from django_datalog.models import store_facts
store_facts(
WorksFor(subject=alice, object=tech_corp),
WorksFor(subject=bob, object=tech_corp),
)
```
### Querying
```python
from django_datalog.models import query
# Find Alice's colleagues
colleagues = list(query(ColleaguesOf(alice, Var("colleague"))))
# With Django Q constraints
managers = list(query(WorksFor(Var("emp", where=Q(is_manager=True)), tech_corp)))
# Complex queries
results = list(query(
ColleaguesOf(Var("emp1"), Var("emp2")),
WorksFor(Var("emp1"), Var("company", where=Q(is_active=True)))
))
```
### Rule Context
Isolate rules for testing or temporary logic:
```python
from django_datalog.models import rule_context
# As context manager
with rule_context():
rule(TestFact(Var("x")), LocalFact(Var("x")))
results = query(TestFact(Var("x"))) # Rules active here
# As decorator
@rule_context
def test_something(self):
rule(TestFact(Var("x")), LocalFact(Var("x")))
assert len(query(TestFact(Var("x")))) > 0
```
### Variables & Constraints
```python
# Basic variable
emp = Var("employee")
# With Django Q constraints
senior_emp = Var("employee", where=Q(years_experience__gte=5))
# Multiple constraints
constrained = Var("emp", where=Q(is_active=True) & Q(department="Engineering"))
```
## Performance Features
### Automatic Optimization
The engine automatically:
- Propagates constraints across same-named variables
- Orders execution by selectivity (most selective first)
- Learns from execution times for better planning
- Pushes constraints to the database
```python
# You write natural queries:
query(
ColleaguesOf(Var("emp1"), Var("emp2", where=Q(department="Engineering"))),
WorksFor(Var("emp1"), Var("company", where=Q(is_active=True)))
)
# Engine automatically optimizes constraint propagation and execution order
```
## Example: Complete Employee System
```python
# models.py
class Employee(models.Model):
name = models.CharField(max_length=100)
is_manager = models.BooleanField(default=False)
class WorksFor(Fact):
subject: Employee | Var
object: Company | Var
class ColleaguesOf(Fact, inferred=True):
subject: Employee | Var
object: Employee | Var
# rules.py
rule(
ColleaguesOf(Var("emp1"), Var("emp2")),
WorksFor(Var("emp1"), Var("company")) & WorksFor(Var("emp2"), Var("company"))
)
# usage.py
store_facts(
WorksFor(subject=alice, object=tech_corp),
WorksFor(subject=bob, object=tech_corp),
)
# Query automatically infers colleagues
colleagues = query(ColleaguesOf(alice, Var("colleague")))
```
## Testing
```python
class MyTest(TestCase):
@rule_context # Isolate rules per test
def test_access_control(self):
rule(CanAccess(Var("user")), IsAdmin(Var("user")))
results = query(CanAccess(admin_user))
self.assertEqual(len(results), 1)
```
## Requirements
- Python 3.10+
- Django 5.0+
## License
MIT License
Raw data
{
"_id": null,
"home_page": null,
"name": "django-datalog",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "datalog, django, facts, inference-engine, logic-programming, query, rules",
"author": null,
"author_email": "Eddy Ernesto del Valle Pino <eddy@edelvalle.me>",
"download_url": "https://files.pythonhosted.org/packages/71/67/99afd71240e20bf94c7170976e3fecfb968461e345f5e27059acf5c28d85/django_datalog-0.3.1.tar.gz",
"platform": null,
"description": "# django-datalog\n\nA logic programming and inference engine for Django applications.\n\n## Installation\n\n```bash\npip install django-datalog\n```\n\n```python\n# settings.py\nINSTALLED_APPS = ['django_datalog']\n```\n\n```bash\npython manage.py migrate\n```\n\n## Core Concepts\n\n### Facts\nDefine facts as Python classes with Django model integration:\n\n```python\nfrom django_datalog.models import Fact, Var\n\nclass WorksFor(Fact):\n subject: Employee | Var # Employee\n object: Company | Var # Company\n\nclass ColleaguesOf(Fact, inferred=True): # Inferred facts can't be stored directly\n subject: Employee | Var\n object: Employee | Var\n```\n\n### Rules\nDefine inference logic with tuples (AND) and lists (OR):\n\n```python\nfrom django_datalog.rules import rule\n\n# Simple rule: Colleagues work at same company\nrule(\n ColleaguesOf(Var(\"emp1\"), Var(\"emp2\")),\n WorksFor(Var(\"emp1\"), Var(\"company\")) & WorksFor(Var(\"emp2\"), Var(\"company\"))\n)\n\n# Disjunctive rule: HasAccess via admin OR manager\nrule(\n HasAccess(Var(\"user\"), Var(\"resource\")),\n IsAdmin(Var(\"user\")) | IsManager(Var(\"user\"), Var(\"resource\"))\n)\n\n# Mixed rule: Complex access control\nrule(\n CanEdit(Var(\"user\"), Var(\"doc\")),\n IsOwner(Var(\"user\"), Var(\"doc\")) | \n (IsManager(Var(\"user\"), Var(\"folder\")) & Contains(Var(\"folder\"), Var(\"doc\")))\n)\n```\n\n### Fact Operators\nUse `|` (OR) and `&` (AND) operators:\n\n```python\n# Modern operator syntax (recommended):\nrule(head, fact1 | fact2) # OR: fact1 OR fact2\nrule(head, fact1 & fact2) # AND: fact1 AND fact2\n\n# Combining operators:\nrule(head, (fact1 & fact2) | fact3) # (fact1 AND fact2) OR fact3\nrule(head, fact1 & fact2 & fact3) # fact1 AND fact2 AND fact3\nrule(head, fact1 | fact2 | fact3) # fact1 OR fact2 OR fact3\n\n# Legacy syntax (still supported):\nrule(head, [fact1, fact2]) # OR (list syntax)\nrule(head, (fact1, fact2)) # AND (tuple syntax)\n```\n\n### Storing Facts\n```python\nfrom django_datalog.models import store_facts\n\nstore_facts(\n WorksFor(subject=alice, object=tech_corp),\n WorksFor(subject=bob, object=tech_corp),\n)\n```\n\n### Querying\n```python\nfrom django_datalog.models import query\n\n# Find Alice's colleagues\ncolleagues = list(query(ColleaguesOf(alice, Var(\"colleague\"))))\n\n# With Django Q constraints\nmanagers = list(query(WorksFor(Var(\"emp\", where=Q(is_manager=True)), tech_corp)))\n\n# Complex queries\nresults = list(query(\n ColleaguesOf(Var(\"emp1\"), Var(\"emp2\")),\n WorksFor(Var(\"emp1\"), Var(\"company\", where=Q(is_active=True)))\n))\n```\n\n### Rule Context\nIsolate rules for testing or temporary logic:\n\n```python\nfrom django_datalog.models import rule_context\n\n# As context manager\nwith rule_context():\n rule(TestFact(Var(\"x\")), LocalFact(Var(\"x\")))\n results = query(TestFact(Var(\"x\"))) # Rules active here\n\n# As decorator\n@rule_context\ndef test_something(self):\n rule(TestFact(Var(\"x\")), LocalFact(Var(\"x\")))\n assert len(query(TestFact(Var(\"x\")))) > 0\n```\n\n### Variables & Constraints\n```python\n# Basic variable\nemp = Var(\"employee\")\n\n# With Django Q constraints\nsenior_emp = Var(\"employee\", where=Q(years_experience__gte=5))\n\n# Multiple constraints\nconstrained = Var(\"emp\", where=Q(is_active=True) & Q(department=\"Engineering\"))\n```\n\n## Performance Features\n\n### Automatic Optimization\nThe engine automatically:\n- Propagates constraints across same-named variables\n- Orders execution by selectivity (most selective first)\n- Learns from execution times for better planning\n- Pushes constraints to the database\n\n```python\n# You write natural queries:\nquery(\n ColleaguesOf(Var(\"emp1\"), Var(\"emp2\", where=Q(department=\"Engineering\"))),\n WorksFor(Var(\"emp1\"), Var(\"company\", where=Q(is_active=True)))\n)\n\n# Engine automatically optimizes constraint propagation and execution order\n```\n\n## Example: Complete Employee System\n\n```python\n# models.py\nclass Employee(models.Model):\n name = models.CharField(max_length=100)\n is_manager = models.BooleanField(default=False)\n\nclass WorksFor(Fact):\n subject: Employee | Var\n object: Company | Var\n\nclass ColleaguesOf(Fact, inferred=True):\n subject: Employee | Var\n object: Employee | Var\n\n# rules.py\nrule(\n ColleaguesOf(Var(\"emp1\"), Var(\"emp2\")),\n WorksFor(Var(\"emp1\"), Var(\"company\")) & WorksFor(Var(\"emp2\"), Var(\"company\"))\n)\n\n# usage.py\nstore_facts(\n WorksFor(subject=alice, object=tech_corp),\n WorksFor(subject=bob, object=tech_corp),\n)\n\n# Query automatically infers colleagues\ncolleagues = query(ColleaguesOf(alice, Var(\"colleague\")))\n```\n\n## Testing\n\n```python\nclass MyTest(TestCase):\n @rule_context # Isolate rules per test\n def test_access_control(self):\n rule(CanAccess(Var(\"user\")), IsAdmin(Var(\"user\")))\n \n results = query(CanAccess(admin_user))\n self.assertEqual(len(results), 1)\n```\n\n## Requirements\n\n- Python 3.10+\n- Django 5.0+\n\n## License\n\nMIT License\n",
"bugtrack_url": null,
"license": null,
"summary": "Django Datalog - Logic programming and inference engine for Django applications",
"version": "0.3.1",
"project_urls": {
"Changelog": "https://github.com/edelvalle/django-datalog/blob/main/CHANGELOG.md",
"Documentation": "https://django-datalog.readthedocs.io/",
"Homepage": "https://github.com/edelvalle/django-datalog",
"Issues": "https://github.com/edelvalle/django-datalog/issues",
"Repository": "https://github.com/edelvalle/django-datalog.git"
},
"split_keywords": [
"datalog",
" django",
" facts",
" inference-engine",
" logic-programming",
" query",
" rules"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "4239bbab5a2d526c5553c72f853df669d23e31a4951c7fbbd8471a22f97a30da",
"md5": "59096b2ceb43672727182c0b48c2ad0e",
"sha256": "c35a639af05ed907e7b017f53b1bd8b48f5c4639026953701d4a7cd2b9fc6c9e"
},
"downloads": -1,
"filename": "django_datalog-0.3.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "59096b2ceb43672727182c0b48c2ad0e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 23101,
"upload_time": "2025-07-23T18:21:56",
"upload_time_iso_8601": "2025-07-23T18:21:56.023504Z",
"url": "https://files.pythonhosted.org/packages/42/39/bbab5a2d526c5553c72f853df669d23e31a4951c7fbbd8471a22f97a30da/django_datalog-0.3.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "716799afd71240e20bf94c7170976e3fecfb968461e345f5e27059acf5c28d85",
"md5": "f2e05d833238bc03708a48e73f20caf8",
"sha256": "00150f0d193092acd2155e5a0d3fd84caa23eda931a91fdbbf8cffef6edc049c"
},
"downloads": -1,
"filename": "django_datalog-0.3.1.tar.gz",
"has_sig": false,
"md5_digest": "f2e05d833238bc03708a48e73f20caf8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 23868,
"upload_time": "2025-07-23T18:21:57",
"upload_time_iso_8601": "2025-07-23T18:21:57.822440Z",
"url": "https://files.pythonhosted.org/packages/71/67/99afd71240e20bf94c7170976e3fecfb968461e345f5e27059acf5c28d85/django_datalog-0.3.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-23 18:21:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "edelvalle",
"github_project": "django-datalog",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "django-datalog"
}