django-datalog 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_datalog-0.1.0/.gitignore +51 -0
- django_datalog-0.1.0/CHANGELOG.md +44 -0
- django_datalog-0.1.0/LICENSE +21 -0
- django_datalog-0.1.0/PKG-INFO +324 -0
- django_datalog-0.1.0/README.md +275 -0
- django_datalog-0.1.0/django_datalog/__init__.py +9 -0
- django_datalog-0.1.0/django_datalog/apps.py +9 -0
- django_datalog-0.1.0/django_datalog/facts.py +164 -0
- django_datalog-0.1.0/django_datalog/migrations/__init__.py +0 -0
- django_datalog-0.1.0/django_datalog/models.py +30 -0
- django_datalog-0.1.0/django_datalog/query.py +394 -0
- django_datalog-0.1.0/django_datalog/rules.py +209 -0
- django_datalog-0.1.0/pyproject.toml +185 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.pyc
|
|
6
|
+
*.pyo
|
|
7
|
+
|
|
8
|
+
# Django
|
|
9
|
+
*.log
|
|
10
|
+
db.sqlite3
|
|
11
|
+
db.sqlite3-journal
|
|
12
|
+
local_settings.py
|
|
13
|
+
|
|
14
|
+
# Testing and coverage
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
.hypothesis/
|
|
19
|
+
|
|
20
|
+
# Virtual environments
|
|
21
|
+
.venv/
|
|
22
|
+
venv/
|
|
23
|
+
env/
|
|
24
|
+
|
|
25
|
+
# Package management
|
|
26
|
+
|
|
27
|
+
# Type checkers
|
|
28
|
+
.mypy_cache/
|
|
29
|
+
.ruff_cache/
|
|
30
|
+
|
|
31
|
+
# IDEs
|
|
32
|
+
.vscode/
|
|
33
|
+
.idea/
|
|
34
|
+
|
|
35
|
+
# Claude Code
|
|
36
|
+
.claude/
|
|
37
|
+
|
|
38
|
+
# macOS
|
|
39
|
+
.DS_Store
|
|
40
|
+
|
|
41
|
+
# Project debug files
|
|
42
|
+
debug_*.py
|
|
43
|
+
minimal_debug.py
|
|
44
|
+
simple_debug.py
|
|
45
|
+
test_fact_equality.py
|
|
46
|
+
test_rule_deduplication.py
|
|
47
|
+
|
|
48
|
+
# Distribution
|
|
49
|
+
build/
|
|
50
|
+
dist/
|
|
51
|
+
*.egg-info/
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2025-01-21
|
|
9
|
+
|
|
10
|
+
Initial release of django-datalog - a complete datalog inference engine for Django applications.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Django Datalog engine with fact-based data modeling
|
|
14
|
+
- Logic programming with inference rules using Python syntax
|
|
15
|
+
- Query system with variable binding and constraint support
|
|
16
|
+
- Q object integration for filtering query results
|
|
17
|
+
- Performance optimizations including query reordering and batch hydration
|
|
18
|
+
- Modular architecture with separate facts, query, and rules modules
|
|
19
|
+
- Comprehensive test suite with family relationship examples
|
|
20
|
+
- Conditional test model loading for package testing
|
|
21
|
+
- Support for both hydrated objects and PK-only queries
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
- **Fact Definition**: Define facts as Python dataclasses with Django model integration
|
|
25
|
+
- **Inference Rules**: Write rules to derive new facts from existing ones
|
|
26
|
+
- **Query Engine**: Query facts with variable binding and automatic inference
|
|
27
|
+
- **Django Q Objects**: Use Django's Q objects to add constraints to query variables
|
|
28
|
+
- **Performance**: Intelligent query planning and batch operations
|
|
29
|
+
- **Testing**: Built-in test framework with example models and facts
|
|
30
|
+
|
|
31
|
+
### Performance
|
|
32
|
+
- Query reordering based on selectivity and variable constraints
|
|
33
|
+
- Batch hydration of model instances to reduce database queries
|
|
34
|
+
- PK-only query mode for improved performance when full objects aren't needed
|
|
35
|
+
- Cached model type metadata to avoid runtime type introspection
|
|
36
|
+
|
|
37
|
+
### Technical
|
|
38
|
+
- Modular package structure separating facts, queries, and rules
|
|
39
|
+
- Automatic Django model generation from fact definitions
|
|
40
|
+
- Django app integration with proper migrations and settings
|
|
41
|
+
- Type hints throughout with support for Union types (Model | Var)
|
|
42
|
+
- Comprehensive error handling and validation
|
|
43
|
+
|
|
44
|
+
[0.1.0]: https://github.com/edelvalle/django-datalog/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-datalog
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Django Datalog - Logic programming and inference engine for Django applications
|
|
5
|
+
Project-URL: Homepage, https://github.com/edelvalle/django-datalog
|
|
6
|
+
Project-URL: Documentation, https://django-datalog.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/edelvalle/django-datalog.git
|
|
8
|
+
Project-URL: Issues, https://github.com/edelvalle/django-datalog/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/edelvalle/django-datalog/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Eddy Ernesto del Valle Pino <eddy@edelvalle.me>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: datalog,django,facts,inference-engine,logic-programming,query,rules
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Framework :: Django
|
|
17
|
+
Classifier: Framework :: Django :: 5.0
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Programming Language :: Python
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
28
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
30
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
|
+
Requires-Python: >=3.10
|
|
32
|
+
Requires-Dist: django>=5
|
|
33
|
+
Requires-Dist: uuid6>=2024.1.12
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: basedpyright>=1.12; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-django>=4.5; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
|
|
42
|
+
Requires-Dist: mkdocs>=1.5; extra == 'docs'
|
|
43
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
44
|
+
Provides-Extra: test
|
|
45
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'test'
|
|
46
|
+
Requires-Dist: pytest-django>=4.5; extra == 'test'
|
|
47
|
+
Requires-Dist: pytest>=7.0; extra == 'test'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# django-datalog
|
|
51
|
+
|
|
52
|
+
Django Datalog - A logic programming and inference engine for Django applications.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **Fact-based data modeling**: Define facts as Python classes that integrate seamlessly with Django models
|
|
57
|
+
- **Logic programming**: Write inference rules using a familiar Python syntax
|
|
58
|
+
- **Query system**: Query facts and derived conclusions with variable binding
|
|
59
|
+
- **Q object constraints**: Filter query results using Django Q objects for powerful filtering
|
|
60
|
+
- **Performance optimized**: Intelligent query reordering and batch hydration for optimal performance
|
|
61
|
+
- **Django integration**: Seamless integration with existing Django models and ORM
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install django-datalog
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Add `django_datalog` to your `INSTALLED_APPS`:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
INSTALLED_APPS = [
|
|
75
|
+
# ... your other apps
|
|
76
|
+
'django_datalog',
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Run migrations to create the datalog fact tables:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python manage.py migrate
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Basic Example: Company Employee Management
|
|
87
|
+
|
|
88
|
+
Let's start with a realistic business scenario - managing employees in a company:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# models.py
|
|
92
|
+
from django.db import models
|
|
93
|
+
from django.contrib.auth.models import User
|
|
94
|
+
from django_datalog.models import Fact, Var
|
|
95
|
+
|
|
96
|
+
class Company(models.Model):
|
|
97
|
+
name = models.CharField(max_length=100)
|
|
98
|
+
|
|
99
|
+
class Department(models.Model):
|
|
100
|
+
name = models.CharField(max_length=100)
|
|
101
|
+
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
|
102
|
+
|
|
103
|
+
class Employee(models.Model):
|
|
104
|
+
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
|
105
|
+
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
|
106
|
+
department = models.ForeignKey(Department, on_delete=models.CASCADE)
|
|
107
|
+
is_manager = models.BooleanField(default=False)
|
|
108
|
+
|
|
109
|
+
# Define Facts - Note: No @dataclass decorator needed!
|
|
110
|
+
class WorksFor(Fact):
|
|
111
|
+
"""Employee works for a company."""
|
|
112
|
+
subject: Employee | Var # Employee
|
|
113
|
+
object: Company | Var # Company
|
|
114
|
+
|
|
115
|
+
class MemberOf(Fact):
|
|
116
|
+
"""Employee is member of a department."""
|
|
117
|
+
subject: Employee | Var # Employee
|
|
118
|
+
object: Department | Var # Department
|
|
119
|
+
|
|
120
|
+
class ColleaguesOf(Fact):
|
|
121
|
+
"""Two employees are colleagues (inferred from working at same company)."""
|
|
122
|
+
subject: Employee | Var # Employee 1
|
|
123
|
+
object: Employee | Var # Employee 2
|
|
124
|
+
|
|
125
|
+
class TeamMates(Fact):
|
|
126
|
+
"""Two employees are teammates (inferred from same department)."""
|
|
127
|
+
subject: Employee | Var # Employee 1
|
|
128
|
+
object: Employee | Var # Employee 2
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# rules.py - Define inference logic
|
|
133
|
+
from django_datalog.rules import rule
|
|
134
|
+
from django_datalog.models import Var
|
|
135
|
+
from .models import WorksFor, MemberOf, ColleaguesOf, TeamMates
|
|
136
|
+
|
|
137
|
+
# Rule: Employees are colleagues if they work for the same company
|
|
138
|
+
rule(
|
|
139
|
+
ColleaguesOf(Var("emp1"), Var("emp2")),
|
|
140
|
+
WorksFor(Var("emp1"), Var("company")),
|
|
141
|
+
WorksFor(Var("emp2"), Var("company")),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Rule: Employees are teammates if they work in the same department
|
|
145
|
+
rule(
|
|
146
|
+
TeamMates(Var("emp1"), Var("emp2")),
|
|
147
|
+
MemberOf(Var("emp1"), Var("department")),
|
|
148
|
+
MemberOf(Var("emp2"), Var("department")),
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# Usage in views.py or management commands
|
|
154
|
+
from django_datalog.models import query, store_facts
|
|
155
|
+
from django.db.models import Q
|
|
156
|
+
|
|
157
|
+
# Store some facts
|
|
158
|
+
tech_corp = Company.objects.create(name="Tech Corp")
|
|
159
|
+
engineering = Department.objects.create(name="Engineering", company=tech_corp)
|
|
160
|
+
|
|
161
|
+
alice_user = User.objects.create(username="alice")
|
|
162
|
+
bob_user = User.objects.create(username="bob")
|
|
163
|
+
|
|
164
|
+
alice = Employee.objects.create(user=alice_user, company=tech_corp, department=engineering, is_manager=True)
|
|
165
|
+
bob = Employee.objects.create(user=bob_user, company=tech_corp, department=engineering)
|
|
166
|
+
|
|
167
|
+
# Store facts about work relationships
|
|
168
|
+
store_facts(
|
|
169
|
+
WorksFor(subject=alice, object=tech_corp),
|
|
170
|
+
WorksFor(subject=bob, object=tech_corp),
|
|
171
|
+
MemberOf(subject=alice, object=engineering),
|
|
172
|
+
MemberOf(subject=bob, object=engineering),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Query: Find all of Alice's colleagues (inferred automatically!)
|
|
176
|
+
colleagues = list(query(ColleaguesOf(alice, Var("colleague"))))
|
|
177
|
+
for result in colleagues:
|
|
178
|
+
colleague = result["colleague"]
|
|
179
|
+
print(f"Alice works with {colleague.user.username}")
|
|
180
|
+
|
|
181
|
+
# Query: Find all teammates in engineering
|
|
182
|
+
teammates = list(query(TeamMates(Var("emp1"), Var("emp2"))))
|
|
183
|
+
for result in teammates:
|
|
184
|
+
emp1, emp2 = result["emp1"], result["emp2"]
|
|
185
|
+
print(f"{emp1.user.username} and {emp2.user.username} are teammates")
|
|
186
|
+
|
|
187
|
+
# Query with constraints: Find managers who work for Tech Corp
|
|
188
|
+
managers = list(query(WorksFor(Var("employee", where=Q(is_manager=True)), tech_corp)))
|
|
189
|
+
for result in managers:
|
|
190
|
+
manager = result["employee"]
|
|
191
|
+
print(f"{manager.user.username} is a manager at {tech_corp.name}")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Advanced Features
|
|
195
|
+
|
|
196
|
+
### Q Object Constraints
|
|
197
|
+
|
|
198
|
+
Use Django Q objects to add powerful filtering to your queries:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from django.db.models import Q
|
|
202
|
+
|
|
203
|
+
# Find senior employees (with complex constraints)
|
|
204
|
+
senior_employees = query(
|
|
205
|
+
WorksFor(
|
|
206
|
+
Var("employee", where=Q(user__date_joined__year__lt=2020) & Q(is_manager=True)),
|
|
207
|
+
Var("company")
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Find employees in specific departments
|
|
212
|
+
engineering_employees = query(
|
|
213
|
+
MemberOf(Var("employee"), Var("dept", where=Q(name__icontains="engineering")))
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Hydration Control
|
|
218
|
+
|
|
219
|
+
Control whether to fetch full model instances or just PKs for better performance:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# Get full objects (default) - includes all related data
|
|
223
|
+
results = list(query(WorksFor(Var("employee"), tech_corp), hydrate=True))
|
|
224
|
+
employee = results[0]["employee"]
|
|
225
|
+
print(employee.user.username) # Full Employee object with related User
|
|
226
|
+
|
|
227
|
+
# Get PKs only (better performance for large datasets)
|
|
228
|
+
results = list(query(WorksFor(Var("employee"), tech_corp), hydrate=False))
|
|
229
|
+
employee_id = results[0]["employee"] # Just the employee ID (integer)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Family Relationships Example
|
|
233
|
+
|
|
234
|
+
Django-datalog also works great for modeling family relationships:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
class Person(models.Model):
|
|
238
|
+
name = models.CharField(max_length=100)
|
|
239
|
+
age = models.IntegerField()
|
|
240
|
+
|
|
241
|
+
class ParentOf(Fact):
|
|
242
|
+
"""Person is parent of another person."""
|
|
243
|
+
subject: Person | Var # Parent
|
|
244
|
+
object: Person | Var # Child
|
|
245
|
+
|
|
246
|
+
class GrandparentOf(Fact):
|
|
247
|
+
"""Person is grandparent of another person (inferred)."""
|
|
248
|
+
subject: Person | Var # Grandparent
|
|
249
|
+
object: Person | Var # Grandchild
|
|
250
|
+
|
|
251
|
+
class SiblingOf(Fact):
|
|
252
|
+
"""Person is sibling of another person (inferred)."""
|
|
253
|
+
subject: Person | Var # Sibling 1
|
|
254
|
+
object: Person | Var # Sibling 2
|
|
255
|
+
|
|
256
|
+
# Define family rules
|
|
257
|
+
rule(
|
|
258
|
+
GrandparentOf(Var("grandparent"), Var("grandchild")),
|
|
259
|
+
ParentOf(Var("grandparent"), Var("parent")),
|
|
260
|
+
ParentOf(Var("parent"), Var("grandchild")),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
rule(
|
|
264
|
+
SiblingOf(Var("person1"), Var("person2")),
|
|
265
|
+
ParentOf(Var("parent"), Var("person1")),
|
|
266
|
+
ParentOf(Var("parent"), Var("person2")),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Usage
|
|
270
|
+
john = Person.objects.create(name="John", age=65)
|
|
271
|
+
alice = Person.objects.create(name="Alice", age=40)
|
|
272
|
+
bob = Person.objects.create(name="Bob", age=15)
|
|
273
|
+
|
|
274
|
+
store_facts(
|
|
275
|
+
ParentOf(subject=john, object=alice),
|
|
276
|
+
ParentOf(subject=alice, object=bob)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Query: Find Bob's grandparents (automatically inferred!)
|
|
280
|
+
grandparents = list(query(GrandparentOf(Var("grandparent"), bob)))
|
|
281
|
+
print(f"Bob's grandparent: {grandparents[0]['grandparent'].name}") # John
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Key Benefits
|
|
285
|
+
|
|
286
|
+
- **No complex SQL**: Express complex relationship logic in simple Python rules
|
|
287
|
+
- **Automatic inference**: New facts are derived automatically from your rules
|
|
288
|
+
- **Performance optimized**: Query reordering and batch loading for optimal performance
|
|
289
|
+
- **Django native**: Works seamlessly with your existing Django models and admin
|
|
290
|
+
- **Type safe**: Full type hints and IDE support with modern Python syntax
|
|
291
|
+
|
|
292
|
+
## Fact Retraction
|
|
293
|
+
|
|
294
|
+
Remove facts when relationships change:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from django_datalog.models import retract_facts
|
|
298
|
+
|
|
299
|
+
# Remove a work relationship
|
|
300
|
+
retract_facts(WorksFor(subject=alice, object=tech_corp))
|
|
301
|
+
|
|
302
|
+
# The system will automatically update inferred facts like ColleaguesOf
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Requirements
|
|
306
|
+
|
|
307
|
+
- Python 3.10+
|
|
308
|
+
- Django 5.0+
|
|
309
|
+
|
|
310
|
+
## Documentation
|
|
311
|
+
|
|
312
|
+
For detailed examples, API reference, and advanced usage, see the [repository documentation](https://github.com/edelvalle/django-datalog) and [CHANGELOG.md](CHANGELOG.md).
|
|
313
|
+
|
|
314
|
+
## Contributing
|
|
315
|
+
|
|
316
|
+
We welcome contributions! Please see our repository on [GitHub](https://github.com/edelvalle/django-datalog) for details.
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
321
|
+
|
|
322
|
+
## Changelog
|
|
323
|
+
|
|
324
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history and changes.
|