🚀 SQLORM
Django's Powerful ORM — Now for Standalone Python Scripts!
Use Django's mature, battle-tested ORM in any Python script without the overhead of a full Django project.
________ ________ ___ ________ ________ _____ ______
|\ ____\|\ __ \|\ \ |\ __ \|\ __ \|\ _ \ _ \
\ \ \___|\ \ \|\ \ \ \ \ \ \|\ \ \ \|\ \ \ \\\__\ \ \
\ \_____ \ \ \\\ \ \ \ \ \ \\\ \ \ _ _\ \ \\|__| \ \
\|____|\ \ \ \\\ \ \ \____\ \ \\\ \ \ \\ \\ \ \ \ \ \
____\_\ \ \_____ \ \_______\ \_______\ \__\\ _\\ \__\ \ \__\
|\_________\|___| \__\|_______|\|_______|\|__|\|__|\|__| \|__|
\|_________| \|__|
🤔 Why SQLORM?
Django's ORM is amazing, but it comes with a catch — you typically need a full Django project structure to use it. That means:
- ❌ Creating
manage.py, settings.py, and app folders
- ❌ Running
django-admin startproject
- ❌ Dealing with
INSTALLED_APPS and migrations infrastructure
SQLORM solves this problem! It wraps Django's ORM with a minimal configuration layer, giving you:
- ✅ Zero Django project structure required — just import and go
- ✅ All Django ORM features — querysets, fields, Q objects, F expressions, aggregations
- ✅ All database backends — SQLite, PostgreSQL, MySQL, Oracle
- ✅ Production-ready — Django is battle-tested at scale (Instagram, Spotify, Mozilla)
- ✅ Familiar API — if you know Django, you already know SQLORM
📦 Installation
Install directly from GitHub:
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
git clone https://github.com/surajsinghbisht054/sqlorm.git
cd sqlorm
pip install -e .
With Database Drivers
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
pip install psycopg2-binary
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
pip install mysqlclient
git clone https://github.com/surajsinghbisht054/sqlorm.git
cd sqlorm
pip install -e ".[dev]"
Requirements
🚀 Quick Start
Here's a complete working example in just a few lines:
from sqlorm import configure, Model, fields, create_tables
configure({
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'todo_app.sqlite3',
})
class Task(Model):
title = fields.CharField(max_length=200)
is_completed = fields.BooleanField(default=False)
created_at = fields.DateTimeField(auto_now_add=True)
create_tables()
task = Task.objects.create(title="Buy groceries")
pending_tasks = Task.objects.filter(is_completed=False)
print(f"Pending tasks: {pending_tasks.count()}")
That's it! No manage.py, no startproject, no INSTALLED_APPS. Just Python.
🛠 CLI Usage (Migrations)
For production applications, you should use migrations instead of create_tables().
sqlorm makemigrations --models models.py
sqlorm migrate --models models.py
📖 Documentation
Table of Contents
Configuration
SQLite (Simplest)
from sqlorm import configure
configure({
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'database.sqlite3',
})
PostgreSQL
configure({
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
})
MySQL
configure({
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mydatabase',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '3306',
})
From Environment Variables
import os
from sqlorm.config import configure_from_env
os.environ['DATABASE_URL'] = 'postgres://user:pass@localhost:5432/mydb'
configure_from_env()
os.environ['SQLORM_DB_ENGINE'] = 'django.db.backends.postgresql'
os.environ['SQLORM_DB_NAME'] = 'mydb'
os.environ['SQLORM_DB_USER'] = 'user'
os.environ['SQLORM_DB_PASSWORD'] = 'pass'
os.environ['SQLORM_DB_HOST'] = 'localhost'
configure_from_env()
From Configuration File
from sqlorm import configure_from_file
configure_from_file('config.json')
configure_from_file('config.yaml')
config.json:
{
"database": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "mydb.sqlite3"
},
"debug": true
}
Defining Models
Models are defined exactly like Django models:
from sqlorm import Model, fields
class Article(Model):
title = fields.CharField(max_length=200)
slug = fields.SlugField(unique=True)
content = fields.TextField()
view_count = fields.PositiveIntegerField(default=0)
rating = fields.DecimalField(max_digits=3, decimal_places=2, null=True)
is_published = fields.BooleanField(default=False)
published_at = fields.DateTimeField(null=True, blank=True)
created_at = fields.DateTimeField(auto_now_add=True)
updated_at = fields.DateTimeField(auto_now=True)
STATUS_CHOICES = [
('draft', 'Draft'),
('review', 'Under Review'),
('published', 'Published'),
]
status = fields.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
class Meta:
ordering = ['-created_at']
verbose_name = 'Article'
verbose_name_plural = 'Articles'
Field Types
All Django field types are available:
CharField | Fixed-length string | fields.CharField(max_length=100) |
TextField | Unlimited text | fields.TextField() |
IntegerField | Integer | fields.IntegerField(default=0) |
FloatField | Floating point | fields.FloatField() |
DecimalField | Fixed precision | fields.DecimalField(max_digits=10, decimal_places=2) |
BooleanField | True/False | fields.BooleanField(default=False) |
DateField | Date | fields.DateField() |
DateTimeField | Date and time | fields.DateTimeField(auto_now_add=True) |
EmailField | Email with validation | fields.EmailField(unique=True) |
URLField | URL with validation | fields.URLField() |
SlugField | URL-friendly string | fields.SlugField(unique=True) |
UUIDField | UUID | fields.UUIDField(default=uuid.uuid4) |
JSONField | JSON data | fields.JSONField(default=dict) |
Common field options:
null=True — Allow NULL in database
blank=True — Allow empty in forms
default=value — Default value
unique=True — Must be unique
db_index=True — Create database index
choices=[...] — Limit to specific values
CRUD Operations
Create
user = User.objects.create(
name="John Doe",
email="john@example.com"
)
user = User(name="Jane Doe", email="jane@example.com")
user.save()
user, created = User.objects.get_or_create(
email="bob@example.com",
defaults={'name': 'Bob Smith'}
)
Read
users = User.objects.all()
user = User.objects.get(id=1)
user = User.objects.get(email="john@example.com")
first = User.objects.first()
last = User.objects.last()
count = User.objects.count()
Update
user = User.objects.get(id=1)
user.name = "John Smith"
user.save()
User.objects.filter(is_active=False).update(is_active=True)
Delete
user = User.objects.get(id=1)
user.delete()
User.objects.filter(is_active=False).delete()
Querying
SQLORM supports the full Django QuerySet API:
User.objects.filter(is_active=True)
User.objects.filter(age__gte=18)
User.objects.filter(name__startswith='J')
User.objects.filter(email__contains='@gmail')
User.objects.exclude(is_active=False)
User.objects.filter(is_active=True).exclude(age__lt=18).order_by('name')
User.objects.order_by('name')
User.objects.order_by('-created_at')
User.objects.all()[:10]
User.objects.all()[10:20]
User.objects.values('name', 'email')
User.objects.values_list('name', flat=True)
User.objects.values('city').distinct()
Lookup Types
User.objects.filter(name='John')
User.objects.filter(name__exact='John')
User.objects.filter(name__iexact='john')
User.objects.filter(name__contains='oh')
User.objects.filter(name__icontains='OH')
User.objects.filter(name__startswith='J')
User.objects.filter(name__endswith='n')
User.objects.filter(age__range=(18, 65))
User.objects.filter(status__in=['active', 'pending'])
User.objects.filter(deleted_at__isnull=True)
User.objects.filter(age__gt=18)
User.objects.filter(age__gte=18)
User.objects.filter(age__lt=65)
User.objects.filter(age__lte=65)
Advanced Features
Q Objects (Complex Queries)
from sqlorm import Q
User.objects.filter(Q(age__lt=18) | Q(age__gt=65))
User.objects.filter(Q(is_active=True) & Q(is_verified=True))
User.objects.filter(~Q(status='banned'))
User.objects.filter(
(Q(age__gte=18) & Q(age__lte=65)) | Q(is_verified=True)
).exclude(Q(status='banned'))
F Expressions (Database Operations)
from sqlorm import F
Product.objects.filter(stock__lt=F('reorder_level'))
Product.objects.update(price=F('price') * 1.1)
Product.objects.annotate(profit=F('price') - F('cost'))
Aggregations
from sqlorm import Count, Sum, Avg, Max, Min
Order.objects.aggregate(total=Sum('amount'))
Order.objects.aggregate(
total=Sum('amount'),
average=Avg('amount'),
count=Count('id'),
max_order=Max('amount'),
min_order=Min('amount'),
)
Order.objects.aggregate(
paid_total=Sum('amount', filter=Q(is_paid=True)),
unpaid_total=Sum('amount', filter=Q(is_paid=False)),
)
Annotations
from sqlorm import Count, Sum
users = User.objects.annotate(
order_count=Count('orders'),
total_spent=Sum('orders__amount'),
)
for user in users:
print(f"{user.name}: {user.order_count} orders, ${user.total_spent}")
Raw SQL
For complex queries that are hard to express with the ORM:
from sqlorm import execute_raw_sql
from sqlorm.connection import execute_raw_sql_dict
results = execute_raw_sql(
"SELECT name, email FROM users WHERE age > %s",
[18]
)
results = execute_raw_sql_dict(
"SELECT name, email FROM users WHERE age > %s",
[18]
)
for row in results:
print(f"{row['name']}: {row['email']}")
execute_raw_sql(
"UPDATE users SET is_active = %s WHERE last_login < %s",
[False, '2024-01-01'],
fetch=False
)
⚠️ Always use parameterized queries to prevent SQL injection!
Transactions
from sqlorm import transaction
with transaction():
user = User.objects.create(name="John", email="john@example.com")
Profile.objects.create(user_id=user.id, bio="Hello!")
try:
with transaction():
User.objects.create(name="Jane", email="jane@example.com")
raise ValueError("Something went wrong!")
except ValueError:
pass
Multiple Databases
from sqlorm import configure
configure({
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'primary.sqlite3',
}, alias='default')
configure({
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'analytics',
'HOST': 'analytics-db.example.com',
'USER': 'readonly',
'PASSWORD': 'secret',
}, alias='analytics')
from sqlorm import get_connection
analytics_conn = get_connection('analytics')
Schema Migrations
SQLORM supports Django's full migration system via the CLI.
1. Setup
Ensure your configure() call includes migrations_dir:
configure(
{...},
migrations_dir='./migrations'
)
2. Create Migrations
When you change your models, run:
sqlorm makemigrations --models your_script.py
This will create migration files in your specified migrations_dir.
3. Apply Migrations
To apply changes to the database:
sqlorm migrate --models your_script.py
This tracks applied migrations in the django_migrations table, just like standard Django.
🔄 Migration from Django
Already using Django? SQLORM is designed to be 100% compatible:
from django.db import models | from sqlorm import fields |
class User(models.Model): | class User(Model): |
models.CharField(...) | fields.CharField(...) |
python manage.py migrate | sqlorm migrate ... |
User.objects.all() | User.objects.all() ✅ Same! |
Your Django knowledge transfers directly!
🆚 Comparison with Other ORMs
| Django-compatible API | ✅ | ❌ | ❌ | ✅ |
| No project structure | ✅ | ✅ | ✅ | ❌ |
| Battle-tested at scale | ✅ | ✅ | ⚠️ | ✅ |
| Learning curve | Low* | High | Medium | Low |
| Multiple DB backends | ✅ | ✅ | ✅ | ✅ |
| Async support | ⚠️ | ✅ | ⚠️ | ⚠️ |
* If you know Django, you already know SQLORM!
📁 Project Structure
sqlorm/
├── sqlorm/
│ ├── __init__.py # Main exports
│ ├── config.py # Configuration management
│ ├── base.py # Model base class
│ ├── fields.py # Field type proxy
│ ├── connection.py # Connection utilities
│ └── exceptions.py # Custom exceptions
├── tests/
│ └── test_sqlorm.py # Test suite
├── examples/
│ ├── basic_usage.py
│ ├── advanced_usage.py
│ ├── postgresql_usage.py
│ └── configuration_examples.py
├── setup.py
├── pyproject.toml
└── README.md
🧪 Running Tests
pip install -e ".[dev]"
pytest tests/ -v
pytest tests/ --cov=sqlorm --cov-report=html
🤝 Contributing
Contributions are welcome! Here's how:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature)
- Commit your changes (
git commit -m 'Add amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
Please make sure to update tests as appropriate.
📚 Resources
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
⭐ If you find SQLORM useful, please give it a star! ⭐
Made with ❤️ for the Python community