🚀 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_APPSand 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:
# Basic installation
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
# Or clone and install locally
git clone https://github.com/surajsinghbisht054/sqlorm.git
cd sqlorm
pip install -e .
With Database Drivers
# Install with PostgreSQL support
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
pip install psycopg2-binary
# Install with MySQL support
pip install git+https://github.com/surajsinghbisht054/sqlorm.git
pip install mysqlclient
# For development (with test dependencies)
git clone https://github.com/surajsinghbisht054/sqlorm.git
cd sqlorm
pip install -e ".[dev]"
Requirements
- Python 3.8+
- Django 3.2+
🚀 Quick Start
Here's a complete working example in just a few lines:
from sqlorm import configure, Model, fields, create_tables
# 1. Configure the database (that's it - no settings.py needed!)
configure({
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'todo_app.sqlite3',
})
# 2. Define your models (exactly like Django!)
class Task(Model):
title = fields.CharField(max_length=200)
is_completed = fields.BooleanField(default=False)
created_at = fields.DateTimeField(auto_now_add=True)
# 3. Create the table (for quick start without migrations)
create_tables()
# 4. Use Django ORM as usual! 🎉
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().
- Create a script with your models (e.g.,
models.py). - Configure with
migrations_dir:configure(..., migrations_dir='./migrations')
- Run commands:
# Create migrations
sqlorm makemigrations --models models.py
# Apply migrations
sqlorm migrate --models models.py
📖 Documentation
Table of Contents
- Configuration
- Defining Models
- Field Types
- CRUD Operations
- Querying
- Advanced Features
- Raw SQL
- Transactions
- Multiple Databases
- Schema Migrations
Configuration
SQLite (Simplest)
from sqlorm import configure
configure({
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'database.sqlite3', # Or ':memory:' for in-memory DB
})
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
# Using DATABASE_URL (Heroku-style)
os.environ['DATABASE_URL'] = 'postgres://user:pass@localhost:5432/mydb'
configure_from_env()
# Or using individual variables
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
# JSON file
configure_from_file('config.json')
# YAML file (requires pyyaml)
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):
# Text fields
title = fields.CharField(max_length=200)
slug = fields.SlugField(unique=True)
content = fields.TextField()
# Numeric fields
view_count = fields.PositiveIntegerField(default=0)
rating = fields.DecimalField(max_digits=3, decimal_places=2, null=True)
# Boolean fields
is_published = fields.BooleanField(default=False)
# Date/time fields
published_at = fields.DateTimeField(null=True, blank=True)
created_at = fields.DateTimeField(auto_now_add=True)
updated_at = fields.DateTimeField(auto_now=True)
# Choices
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:
| Field Type | Description | Example |
|---|---|---|
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 databaseblank=True— Allow empty in formsdefault=value— Default valueunique=True— Must be uniquedb_index=True— Create database indexchoices=[...]— Limit to specific values
CRUD Operations
Create
# Method 1: create()
user = User.objects.create(
name="John Doe",
email="john@example.com"
)
# Method 2: Instantiate and save
user = User(name="Jane Doe", email="jane@example.com")
user.save()
# Method 3: get_or_create
user, created = User.objects.get_or_create(
email="bob@example.com",
defaults={'name': 'Bob Smith'}
)
Read
# Get all records
users = User.objects.all()
# Get single record
user = User.objects.get(id=1)
user = User.objects.get(email="john@example.com")
# Get first/last
first = User.objects.first()
last = User.objects.last()
# Count
count = User.objects.count()
Update
# Single object
user = User.objects.get(id=1)
user.name = "John Smith"
user.save()
# Bulk update
User.objects.filter(is_active=False).update(is_active=True)
Delete
# Single object
user = User.objects.get(id=1)
user.delete()
# Bulk delete
User.objects.filter(is_active=False).delete()
Querying
SQLORM supports the full Django QuerySet API:
# Filtering
User.objects.filter(is_active=True)
User.objects.filter(age__gte=18)
User.objects.filter(name__startswith='J')
User.objects.filter(email__contains='@gmail')
# Excluding
User.objects.exclude(is_active=False)
# Chaining
User.objects.filter(is_active=True).exclude(age__lt=18).order_by('name')
# Ordering
User.objects.order_by('name') # Ascending
User.objects.order_by('-created_at') # Descending
# Limiting
User.objects.all()[:10] # First 10
User.objects.all()[10:20] # 10-20
# Values
User.objects.values('name', 'email')
User.objects.values_list('name', flat=True)
# Distinct
User.objects.values('city').distinct()
Lookup Types
# Exact match
User.objects.filter(name='John')
User.objects.filter(name__exact='John')
# Case-insensitive
User.objects.filter(name__iexact='john')
# Contains
User.objects.filter(name__contains='oh')
User.objects.filter(name__icontains='OH')
# Starts/ends with
User.objects.filter(name__startswith='J')
User.objects.filter(name__endswith='n')
# Range
User.objects.filter(age__range=(18, 65))
# In list
User.objects.filter(status__in=['active', 'pending'])
# Is null
User.objects.filter(deleted_at__isnull=True)
# Greater/less than
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
# OR queries
User.objects.filter(Q(age__lt=18) | Q(age__gt=65))
# AND queries (explicit)
User.objects.filter(Q(is_active=True) & Q(is_verified=True))
# NOT queries
User.objects.filter(~Q(status='banned'))
# Complex combinations
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
# Reference other fields
Product.objects.filter(stock__lt=F('reorder_level'))
# Arithmetic
Product.objects.update(price=F('price') * 1.1) # 10% increase
# Annotations
Product.objects.annotate(profit=F('price') - F('cost'))
Aggregations
from sqlorm import Count, Sum, Avg, Max, Min
# Single aggregation
Order.objects.aggregate(total=Sum('amount'))
# {'total': Decimal('15420.00')}
# Multiple aggregations
Order.objects.aggregate(
total=Sum('amount'),
average=Avg('amount'),
count=Count('id'),
max_order=Max('amount'),
min_order=Min('amount'),
)
# Conditional aggregation
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
# Add computed fields
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
# Execute and get tuples
results = execute_raw_sql(
"SELECT name, email FROM users WHERE age > %s",
[18]
)
# Execute and get dictionaries
results = execute_raw_sql_dict(
"SELECT name, email FROM users WHERE age > %s",
[18]
)
for row in results:
print(f"{row['name']}: {row['email']}")
# Insert/Update (no fetch)
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
# Basic transaction
with transaction():
user = User.objects.create(name="John", email="john@example.com")
Profile.objects.create(user_id=user.id, bio="Hello!")
# Both are committed together, or both are rolled back
# Handling errors
try:
with transaction():
User.objects.create(name="Jane", email="jane@example.com")
raise ValueError("Something went wrong!")
except ValueError:
pass # Transaction was automatically rolled back
Multiple Databases
from sqlorm import configure
# Configure with alias
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')
# Use specific database
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:
| Django | SQLORM |
|---|---|
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
| Feature | SQLORM | SQLAlchemy | Peewee | Raw Django |
|---|---|---|---|---|
| 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
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
# With coverage
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
- Django ORM Documentation: https://docs.djangoproject.com/en/stable/topics/db/
- Django QuerySet API: https://docs.djangoproject.com/en/stable/ref/models/querysets/
- Django Field Types: https://docs.djangoproject.com/en/stable/ref/models/fields/
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
- Email: surajsinghbisht054@gmail.com
- GitHub: @surajsinghbisht054
⭐ If you find SQLORM useful, please give it a star! ⭐
Made with ❤️ for the Python community
Release files for django-sqlorm 3.0.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 | |
|---|---|---|---|
| django_sqlorm-3.0.0.tar.gz | 16.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_sqlorm-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.7 kB
Release files / django_sqlorm-3.0.0.tar.gz
| Download URL | django_sqlorm-3.0.0.tar.gz |
|---|---|
| Size | 16.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3d954f657516e2f73c8dd7b368b39e057ac89f2d5e2036229d376a8d571ff448
|
|
BLAKE2b-256 checksum How to use checksums |
83155cf23b7fd8ed1acceb5a2175d1ac8abe2b16cefa930d999167462e924f43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|
Release files / django_sqlorm-3.0.0-py3-none-any.whl
| Download URL | django_sqlorm-3.0.0-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e15f4ef583597040d4f35c6e66428d996e4471c3b613a53fa49a59d843742112
|
|
BLAKE2b-256 checksum How to use checksums |
059b7d2e07a5cda31ed65ed80ccb5cc7fc9ef6ab54d725a6dcabdea0a10a7ffa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|