Sqlrustler is a lightweight, high-performance Object-Relational Mapping (ORM) library for Python, designed to simplify database interactions with PostgreSQL and MySQL. Built with Rust bindings using maturin for speed and reliability, it offers a Django-inspired API with a focus on modularity, extensibility, and ease of use. Whether you're querying complex relationships, performing bulk operations, or annotating results, sqlrustler provides a robust and intuitive interface.
- ORM with Model Support: Define database tables as Python classes with fields like
IntegerField,TextField, andForeignKeyField... - Query Builder: Chainable query methods (
filter,select,annotate, etc.) for expressive SQL generation. - Database Support: Compatible with PostgreSQL and MySQL, with extensible adapters for other databases.
- Performance: Leverages Rust for critical operations, ensuring fast query execution and result parsing.
- Modular Design: Separates query construction, result parsing, and expression handling for maintainability.
- Raw and Flexible Results: Supports both model instances and raw dictionaries for non-standard queries.
- Annotations and Aggregations: Easily add computed fields (e.g.,
ROW_NUMBER) or aggregate functions (e.g.,COUNT,SUM). - Foreign Key Handling: Seamless
select_relatedandprefetch_relatedfor efficient relationship queries. - Error Handling: Graceful fallback to raw results when model parsing fails, with detailed logging.
- Python 3.8+
- Rust (for building the library)
- PostgreSQL or MySQL server
maturinfor Rust-Python integration
-
Clone the repository:
git clone https://github.com/DVNghiem/SqlRustler.git cd SqlRustler -
Install Python dependencies (if any):
pip3 install poetry maturin[patchelf] poetry install
-
Build and install the library using
maturin:maturin develop
Create a models.py file to define your database tables:
from sqlrustler.model import Model
from sqlrustler.field import IntegerField, TextField, ForeignKeyField
class Company(Model):
__tablename__ = "res_company"
__alias__ = "default"
id = IntegerField(primary_key=True)
name = TextField()
class ResPartner(Model):
__tablename__ = "res_partner"
__alias__ = "default"
id = IntegerField(primary_key=True)
name = TextField()
email = TextField()
company_id = ForeignKeyField(Company, related_field="id")Configure and connect to your database:
from sqlrustler import DatabaseConfig, DatabaseType, DatabaseConnection
config = DatabaseConfig(
driver=DatabaseType.Postgres,
url="postgresql://user:password@localhost:5432/stag_v2",
max_connections=10,
min_connections=1,
idle_timeout=30,
)
DatabaseConnection.connect(config)Perform queries using the ORM:
from sqlrustler import F
# Fetch all partners with row numbers
partners = ResPartner.objects().annotate(row_num=F("id").row_number()).execute()
for partner in partners:
print(f"Partner: {partner.name}, Row Number: {partner._annotations['row_num']}")
# Filter and select related data
partners = ResPartner.objects().filter(name__contains="John").select_related("company_id").execute()
for partner in partners:
print(f"Partner: {partner.name}, Company: {partner.company_id.name}")
# Aggregate data
result = ResPartner.objects().aggregate(count=F("id").count())
print(f"Total partners: {result['count']}")
# Raw results for custom queries
results = ResPartner.objects().select("name", "email").raw().execute()
print(results) # [{'name': 'John Doe', 'email': 'john@example.com'}, ...]# Filter partners by email and order by name
partners = ResPartner.objects().filter(email__endswith="@example.com").order_by("name").execute()company = Company(name="Test Corp")
company.save()
partners = [
ResPartner(name=f"User {i}", email=f"user{i}@example.com", company_id=company)
for i in range(3)
]
ResPartner.objects().bulk_create(partners)# Select specific fields as dictionaries
results = ResPartner.objects().values("name", "email").execute()
# [{'name': 'John Doe', 'email': 'john@example.com'}, ...]
# Flat values list
names = ResPartner.objects().values_list("name", flat=True).execute()
# ['John Doe', 'Jane Smith', ...]# Rank partners by company
partners = ResPartner.objects().annotate(
rank=F("id").rank(partition_by=["company_id"])
).execute()
for partner in partners:
print(f"Partner: {partner.name}, Rank: {partner._annotations['rank']}")- QuerySet: Main interface for building and executing queries.
- Methods:
filter,exclude,select,annotate,aggregate,select_related,prefetch_related,raw,values,execute, etc.
- Methods:
- Model: Base class for defining database tables.
- Fields:
IntegerField,TextField,ForeignKeyField, etc., for defining model attributes. - DatabaseConfig: Configures database connections.
QuerySet.raw(): Return raw dictionaries instead of model instances.QuerySet.values(*fields): Return dictionaries for specified fields.QuerySet.annotate(**annotations): Add computed fields (e.g.,row_num).QuerySet.select_related(*fields): Eagerly load foreign key relationships.QuerySet.bulk_create(objs): Efficiently insert multiple records.
We welcome contributions! To get started:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/your-feature). - Commit your changes (
git commit -m "Add your feature"). - Push to the branch (
git push origin feature/your-feature). - Open a Pull Request.
Please include tests for new features and follow the coding style in the codebase.
This project is licensed under the MIT License. See the LICENSE file for details.
For issues, feature requests, or questions, please open an issue on the GitHub repository.