RpRelPyDBRelational Python

Documentation

1. Full Tutorial

Developer-focused documentation with concepts, function details, examples, outputs and SQL comparisons.

Full tutorial: a small shop database

This tutorial builds a realistic mini-domain: users, products, orders and order items. It demonstrates primary keys, foreign keys, joins, grouping, indexes, persistence, SQL export and encrypted email.

1. Create schema

from relpy import RelPy, AutoNumber, col
from relpy import count, sum_, avg

key = RelPy.generate_encryption_key()
db = RelPy(encryption_key=key)

# Users
_db = db.create_table("users")
db.add_column("users", "id", AutoNumber, is_primary_key=True)
db.add_column("users", "name", str, nullable=False)
db.add_column("users", "email", str, nullable=False, is_encrypted=True)

# Products
db.create_table("products")
db.add_column("products", "id", AutoNumber, is_primary_key=True)
db.add_column("products", "name", str, nullable=False)
db.add_column("products", "price", float, nullable=False)

# Orders
db.create_table("orders")
db.add_column("orders", "id", AutoNumber, is_primary_key=True)
db.add_column("orders", "user_id", int, nullable=False, references="users.id", on_delete="CASCADE")
db.add_column("orders", "status", str, nullable=False)

# Order items - composite primary key
db.create_table("order_items")
db.add_column("order_items", "order_id", int, nullable=False, references="orders.id", on_delete="CASCADE")
db.add_column("order_items", "product_id", int, nullable=False, references="products.id")
db.add_column("order_items", "quantity", int, nullable=False)
db.set_primary_key("order_items", ["order_id", "product_id"])
Why this matters: RelPyDB stores schema separately from data, so constraints are known before data is inserted.

2. Insert rows

alice = db.insert("users", {"name": "Alice", "email": "alice@example.com"})
bob = db.insert("users", {"name": "Bob", "email": "bob@example.com"})

keyboard = db.insert("products", {"name": "Keyboard", "price": 120.0})
mouse = db.insert("products", {"name": "Mouse", "price": 80.0})

order1 = db.insert("orders", {"user_id": alice["id"], "status": "paid"})
order2 = db.insert("orders", {"user_id": bob["id"], "status": "pending"})

db.insert("order_items", {"order_id": order1["id"], "product_id": keyboard["id"], "quantity": 1})
db.insert("order_items", {"order_id": order1["id"], "product_id": mouse["id"], "quantity": 2})
db.insert("order_items", {"order_id": order2["id"], "product_id": mouse["id"], "quantity": 1})

Foreign keys are validated on insert. A row that references a missing users.id, orders.id or products.id will fail.

3. Query one table

paid_orders = (
    db.query("orders")
      .where(col("status") == "paid")
      .to_list()
)
SELECT *
FROM orders
WHERE status = 'paid';
Output
[{"id": 1, "user_id": 1, "status": "paid"}]

4. Join tables

rows = (
    db.query("orders")
      .join("users")
      .where(col("orders.status") == "paid")
      .select("orders.id", "users.name", "orders.status")
      .to_list(decrypt=True)
)

Because orders.user_id references users.id, RelPyDB can infer the join automatically.

Output
[{"orders.id": 1, "users.name": "Alice", "orders.status": "paid"}]

5. Group and aggregate

summary = (
    db.query("orders")
      .group_by("status")
      .aggregate(
          order_count=count(),
      )
      .order_by("status")
      .to_list()
)
Output
[{"status": "paid", "order_count": 1}, {"status": "pending", "order_count": 1}]

6. Index encrypted email and query naturally

db.create_index("users", "email")

user = (
    db.query("users")
      .where(col("email") == "alice@example.com")
      .one(decrypt=True)
)

The email is stored encrypted, but equality lookup can use a blind index.

7. Save, load and export SQL

db.save("shop.relpy.json")

loaded_db = RelPy.load("shop.relpy.json", encryption_key=key)
print(loaded_db.to_list("users"))
print(loaded_db.to_list("users", decrypt=True))
print(loaded_db.to_sql("users", decrypt=True))
Important: the key is never saved in the JSON file. Keep it separately.