Skip to content

mydborm

PyPI version Python License: MIT Tests Coverage PyPI Downloads

mydborm is a Python library that lets you work with a database using plain Python objects instead of writing raw SQL.

That kind of library is called an ORM — "Object-Relational Mapper." Instead of writing:

INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');

you write:

User.create(username="alice", email="alice@example.com")

Same result, but it reads like normal Python, gets type-checked and validated before it ever touches the database, and doesn't require you to hand-write SQL strings throughout your codebase.

mydborm works with MySQL 8+, PostgreSQL, and YugabyteDB (a distributed database that speaks the PostgreSQL protocol). You write your model once, and the same code runs against any of the three — handy if you develop against MySQL locally but deploy to YugabyteDB in production, or vice versa.

If you're brand new to mydborm, the Quickstart walks through defining a model and running your first queries in a few minutes. This page is a tour of what's available.


Install

pip install mydborm                          # core ORM — works right away
pip install mydborm[cli]                     # + the `mydborm` command-line tool
pip install mydborm[async]                   # + async/await support (for FastAPI etc.)
pip install mydborm[security]                # + password hashing & field encryption
pip install mydborm[dev,cli,async,security]  # everything, useful for contributing

If you only need the basics, pip install mydborm is enough — the extras (cli, async, security) pull in additional dependencies only used by those specific features, so you don't have to install things you won't use.


A first look

This is the entire lifecycle of a simple User model — connect, define, create the table, and use it:

from mydborm import db, BaseModel, IntField, StrField, BoolField
from mydborm import PasswordField, EmailValidator, RangeValidator

# 1. Tell mydborm which database to talk to.
#    "dialect" just means which database engine — "mysql", "postgres", or "yugabyte".
db.configure(
    dialect  = "mysql",
    host     = "127.0.0.1",
    port     = 3306,
    user     = "root",
    password = "yourpassword",
    database = "mydb",
    charset  = "utf8mb4",
)

# 2. Describe your table as a Python class.
#    Each Field below is one column — it also validates values for you.
class User(BaseModel):
    __tablename__ = "users"
    id       = IntField(primary_key=True)
    username = StrField(max_length=50,  nullable=False, unique=True)
    email    = StrField(max_length=255, nullable=False,
                        validators=[EmailValidator()])
    age      = IntField(nullable=True,
                        validators=[RangeValidator(min_val=13, max_val=120)])
    password = PasswordField(nullable=False)   # stored as a bcrypt hash, never plain text
    active   = BoolField(default=True)

# 3. Create the actual table in the database (run this once).
User.create_table()

# 4. Use it like a normal Python object.
uid = User.create(
    username = "alice",
    email    = "alice@example.com",
    age      = 28,
    password = "mysecretpass",   # hashed automatically before it's stored
)

user = User.get(id=uid)
if PasswordField.verify("mysecretpass", user["password"]):
    print("Login successful!")

# 5. Query with method chaining instead of writing SQL.
active_users = (User.query()
                    .where("active", True)
                    .order_by("username")
                    .limit(10)
                    .all())

A few things worth calling out for anyone new to this:

  • db.configure(...) only needs to run once, usually when your app starts.
  • Each Field (IntField, StrField, BoolField, ...) describes one database column and validates Python values before they're saved — if you try to save age=200, the RangeValidator above raises an error instead of silently writing bad data.
  • User.create_table() only needs to run once per database (or each time your schema changes) — it's not something you call on every request.
  • Everything after that — .create(), .get(), .query()... — is what you'll actually use day-to-day.

Continue with the Quickstart for a slower, step-by-step walkthrough, or jump straight to the topic you need in the Guide section in the sidebar.


What's included

Feature What it gives you
Declarative models Define a table as a Python class instead of SQL DDL
29 field types Typed columns — from IntField to PasswordField (bcrypt) and EncryptedField (AES) — see Fields
Full CRUD .create(), .get(), .all(), .filter(), .update(), .delete()
QueryBuilder Chainable .where(), .order_by(), .limit(), joins, group_by, subqueries — see Query Builder
Relationships has_many, belongs_to, many_to_many, with lazy or eager loading — see Relationships
Sessions Track changes to several objects and save them together — see Session
Bulk operations Insert/update/delete thousands of rows in safe chunks, with automatic retry — see Bulk Operations
Transactions Group statements so they all succeed or all roll back together — see Transactions
Schema migrations Detect model changes and generate the SQL to apply them — see Migrations
Database-to-database migration Move schema + data between MySQL, YugabyteDB, and PostgreSQL — see Database Migration
Validators Built-in email/URL/range/length/choice checks, or write your own — see Validators
Security fields Password hashing (bcrypt) and two-way field encryption (AES) — see Security
Async support AsyncBaseModel for use with FastAPI and other async frameworks — see Async
CLI A mydborm command-line tool for connecting, inspecting, and migrating without writing a script — see CLI
Custom exceptions Specific, catchable error types instead of generic exceptions — see Exceptions

mydborm currently has 1,000+ tests with 96% coverage, and is tested against Python 3.9, 3.10, 3.11, and 3.12.


Why mydborm instead of a bigger ORM?

If you've heard of SQLAlchemy or Peewee, here's the short version of how mydborm compares. None of these are "better" in every way — it's a tradeoff between flexibility and simplicity.

mydborm SQLAlchemy Peewee
Install size 47 KB 3 MB 800 KB
MySQL + PostgreSQL + YugabyteDB
Async built-in needs an extra package
Command-line tool included
Password hashing built in
Field-level encryption built in

mydborm trades some of SQLAlchemy's flexibility (it doesn't support every database SQLAlchemy does, and it's less configurable under the hood) for a much smaller footprint and things you'd otherwise have to bolt on yourself — async support, a CLI, password hashing, and distributed-SQL (YugabyteDB) support, all included from the start.

Pick mydborm if you want to be productive in MySQL, PostgreSQL, or YugabyteDB quickly without learning a large framework. Pick SQLAlchemy if you need to support many different databases or want fine-grained control over query generation.