# async-easy-model
A simplified SQLModel-based ORM for async database operations in Python. async-easy-model provides a clean, intuitive interface for common database operations while leveraging the power of SQLModel and SQLAlchemy.
<p align="center">
<img src="https://img.shields.io/pypi/v/async-easy-model" alt="PyPI Version">
<img src="https://img.shields.io/pypi/pyversions/async-easy-model" alt="Python Versions">
<img src="https://img.shields.io/github/license/puntorigen/easy_model" alt="License">
</p>
## Features
- 🚀 Easy-to-use async database operations with standardized methods
- 🔄 Intuitive APIs with sensible defaults for rapid development
- 📊 Dictionary-based CRUD operations (select, insert, update, delete)
- 🔗 Enhanced relationship handling with eager loading and nested operations
- 🔍 Powerful query methods with flexible ordering support
- ⚙️ Automatic relationship detection and bidirectional setup
- 📱 Support for PostgreSQL, SQLite, and MySQL databases
- 🛠️ Built on top of SQLModel and SQLAlchemy for robust performance
- 📝 Type hints for better IDE support
- 🕒 Automatic `id`, `created_at` and `updated_at` fields provided by default
- ⏰ **PostgreSQL DateTime Compatibility**: Automatic timezone-aware to timezone-naive datetime conversion for PostgreSQL TIMESTAMP WITHOUT TIME ZONE columns
- 🔄 Automatic schema migrations for evolving database models
- 📊 Visualization of database schema using Mermaid ER diagrams
- 📋 **JSON Column Support**: Native support for JSON columns with proper serialization in migrations
## Installation
```bash
pip install async-easy-model
```
## Basic Usage
```python
from async_easy_model import EasyModel, init_db, db_config, Field
from typing import Optional
from datetime import datetime
# Configure your database
db_config.configure_sqlite("database.db")
# Define your model
class User(EasyModel, table=True):
#no need to specify id, created_at or updated_at since EasyModel provides them by default
username: str = Field(unique=True)
email: str
# Initialize your database
async def setup():
await init_db()
# Use it in your async code
async def main():
await setup()
# Create a new user
user = await User.insert({
"username": "john_doe",
"email": "john@example.com"
})
# Get user ID
print(f"New user id: {user.id}")
```
## CRUD Operations
First, let's define some models that we'll use throughout the examples:
```python
from async_easy_model import EasyModel, Field
from typing import Optional, List
from datetime import datetime
class User(EasyModel, table=True):
username: str = Field(unique=True)
email: str
is_active: bool = Field(default=True)
class Post(EasyModel, table=True):
title: str
content: str
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
class Comment(EasyModel, table=True):
text: str
post_id: Optional[int] = Field(default=None, foreign_key="post.id")
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
class Department(EasyModel, table=True):
name: str = Field(unique=True)
class Product(EasyModel, table=True):
name: str
price: float
sales: int = Field(default=0)
class Book(EasyModel, table=True):
title: str
author_id: Optional[int] = Field(default=None, foreign_key="author.id")
class Author(EasyModel, table=True):
name: str
```
### Create (Insert)
```python
# Insert a single record
user = await User.insert({
"username": "john_doe",
"email": "john@example.com"
})
# Insert multiple records
users = await User.insert([
{"username": "user1", "email": "user1@example.com"},
{"username": "user2", "email": "user2@example.com"}
])
# Insert with nested relationships
new_post = await Post.insert({
"title": "My Post",
"content": "Content here",
"user": {"username": "jane_doe"}, # Will automatically link to existing user
"comments": [ # Create multiple comments in a single transaction
{"text": "Great post!", "user": {"username": "reader1"}},
{"text": "Thanks for sharing", "user": {"username": "reader2"}}
]
})
# Access nested data without requerying
print(f"Post by {new_post.user.username} with {len(new_post.comments)} comments")
# Insert with nested one-to-many relationships
publisher = await Publisher.insert({
"name": "Example Publisher",
"books": [ # List of nested objects
{
"title": "Python Mastery",
"genres": [
{"name": "Programming"},
{"name": "Education"}
]
},
{"title": "Data Science Handbook"}
]
})
# Access nested relationships immediately
print(f"Publisher: {publisher.name} with {len(publisher.books)} books")
print(f"First book genres: {[g.name for g in publisher.books[0].genres]}")
```
### Read (Retrieve)
```python
# Select by ID
user = await User.select({"id": 1})
# Select with criteria
users = await User.select({"is_active": True}, all=True)
# Select first matching record
first_user = await User.select({"is_active": True}, first=True)
# Select all records
all_users = await User.select({}, all=True)
# Select with wildcard pattern matching
gmail_users = await User.select({"email": "*@gmail.com"}, all=True)
# Select with ordering
recent_users = await User.select({}, order_by="-created_at", all=True)
# Select with limit
latest_posts = await Post.select({}, order_by="-created_at", limit=5)
# Note: limit > 1 automatically sets all=True
# Select with multiple ordering fields
sorted_users = await User.select({}, order_by=["last_name", "first_name"], all=True)
# Select with relationship ordering
posts_by_author = await Post.select({}, order_by="user.username", all=True)
```
### Update
```python
# Update by ID
user = await User.update({"is_active": False}, 1)
# Update by criteria
count = await User.update(
{"is_active": False},
{"last_login": None} # Set all users without login to inactive
)
# Update with relationships
await User.update(
{"department": {"name": "Sales"}}, # Update department relationship
{"username": "john_doe"}
)
```
### Delete
```python
# Delete by ID
success = await User.delete(1)
# Delete by criteria
deleted_count = await User.delete({"is_active": False})
# Delete with compound criteria
await Post.delete({"user": {"username": "john_doe"}, "is_published": False})
```
## Database Schema Visualization
The package includes a `ModelVisualizer` class that makes it easy to generate Entity-Relationship (ER) diagrams for your database models using Mermaid syntax.
```python
from async_easy_model import EasyModel, init_db, db_config, ModelVisualizer
# Initialize your models and database
await init_db()
# Create a visualizer
visualizer = ModelVisualizer()
# Generate a Mermaid ER diagram
er_diagram = visualizer.mermaid()
print(er_diagram)
# Generate a shareable link to view the diagram online
er_link = visualizer.mermaid_link()
print(er_link)
# Customize the diagram title
visualizer.set_title("My Project Database Schema")
custom_diagram = visualizer.mermaid()
```
### Example Mermaid ER Diagram Output
```mermaid
---
title: EasyModel Table Schemas
config:
layout: elk
---
erDiagram
author {
number id PK
string name "required"
string email
}
book {
number id PK
string title "required"
number author_id FK
string isbn
number published_year
author author "virtual"
tag[] tags "virtual"
}
tag {
number id PK
string name "required"
book[] books "virtual"
}
book_tag {
number id PK
number book_id FK "required"
number tag_id FK "required"
book book "virtual"
tag tag "virtual"
}
review {
number id PK
number book_id FK "required"
number rating "required"
string comment
string reviewer_name "required"
book book "virtual"
}
book ||--o{ author : "author_id"
book_tag ||--o{ book : "book_id"
book_tag ||--o{ tag : "tag_id"
book }o--o{ tag : "many-to-many"
review ||--o{ book : "book_id"
```
The diagram automatically:
- Shows all tables with their fields and data types
- Identifies primary keys (PK) and foreign keys (FK)
- Shows required fields and virtual relationships
- Visualizes relationships between tables with proper cardinality
- Properly handles many-to-many relationships
## Convenient Query Methods
async-easy-model provides simplified methods for common query patterns:
```python
# Get all records with relationships loaded (default)
users = await User.all()
# Get all records ordered by a field (ascending)
users = await User.all(order_by="username")
# Get all records ordered by a field (descending)
newest_users = await User.all(order_by="-created_at")
# Get all records ordered by multiple fields
sorted_users = await User.all(order_by=["last_name", "first_name"])
# Get all records ordered by relationship fields
books = await Book.all(order_by="author.name")
# Get the first record
user = await User.first()
# Get the most recently created user
newest_user = await User.first(order_by="-created_at")
# Get a limited number of records
recent_users = await User.limit(10)
# Get a limited number of records with ordering
top_products = await Product.limit(5, order_by="-sales")
```
## Enhanced Relationship Handling
Using the models defined earlier, here's how to work with relationships:
```python
# Load all relationships automatically
post = await Post.select({"id": 1})
print(post.user.username) # Access related objects directly
# Load specific relationships
post = await Post.get_with_related(1, ["user", "comments"])
# Load relationships after fetching
post = await Post.select({"id": 1}, include_relationships=False)
await post.load_related(["user", "comments"])
# Insert with nested relationships
new_post = await Post.insert({
"title": "My Post",
"content": "Content here",
"user": {"username": "jane_doe"}, # Will automatically link to existing user
"comments": [ # Create multiple comments in a single transaction
{"text": "Great post!", "user": {"username": "reader1"}},
{"text": "Thanks for sharing", "user": {"username": "reader2"}}
]
})
# Access nested data without requerying
print(f"Post by {new_post.user.username} with {len(new_post.comments)} comments")
# Convert to dictionary with nested relationships
post_dict = post.to_dict(include_relationships=True, max_depth=2)
```
## Automatic Relationship Detection
The package can automatically detect and set up bidirectional relationships between models:
```python
class User(EasyModel, table=True):
username: str
class Post(EasyModel, table=True):
title: str
user_id: int = Field(foreign_key="user.id")
# After init_db():
# - post.user relationship is automatically available
# - user.posts relationship is automatically available
```
## Database Configuration
```python
# SQLite Configuration
db_config.configure_sqlite("database.db")
db_config.configure_sqlite(":memory:") # In-memory database
# PostgreSQL Configuration
db_config.configure_postgres(
user="your_user",
password="your_password",
host="localhost",
port="5432",
database="your_database"
)
# MySQL Configuration
db_config.configure_mysql(
user="your_user",
password="your_password",
host="localhost",
port="3306",
database="your_database"
)
# Custom Connection URL
db_config.set_connection_url("postgresql+asyncpg://user:password@localhost:5432/database")
```
### Configurable Default for Relationship Loading
**New in v0.3.9**: You can now set a global default for `include_relationships` behavior across all query methods:
```python
# Configure with default_include_relationships=False for better performance
db_config.configure_sqlite("database.db", default_include_relationships=False)
# Or for PostgreSQL
db_config.configure_postgres(
user="your_user",
password="your_password",
host="localhost",
port="5432",
database="your_database",
default_include_relationships=False # Set global default
)
# Or for MySQL
db_config.configure_mysql(
user="your_user",
password="your_password",
host="localhost",
port="3306",
database="your_database",
default_include_relationships=False # Set global default
)
```
**Benefits:**
- **Performance**: Set `default_include_relationships=False` to avoid loading relationships by default, improving query performance
- **Flexibility**: Still override per method call with explicit `True` or `False` values
- **Backward Compatible**: Defaults to `True` if not specified, maintaining existing behavior
**Usage Examples:**
```python
# With default_include_relationships=False configured:
users = await User.all() # No relationships loaded (faster)
users_with_rels = await User.all(include_relationships=True) # Override to load relationships
# With default_include_relationships=True configured (default behavior):
users = await User.all() # Relationships loaded
users_no_rels = await User.all(include_relationships=False) # Override to skip relationships
```
### Database Initialization Options
**New in v0.4.1**: The `init_db()` function now supports configurable auto-relationships handling:
```python
# Default behavior (auto-detect auto-relationships availability)
await init_db()
# Force enable auto-relationships (will warn if not available)
await init_db(has_auto_relationships=True)
# Force disable auto-relationships
await init_db(has_auto_relationships=False)
# Combined with other parameters
await init_db(
migrate=True,
model_classes=[User, Post, Comment],
has_auto_relationships=False
)
```
**Benefits:**
- **Control**: Explicitly enable or disable auto-relationships functionality
- **Reliability**: Auto-relationships errors now issue warnings instead of stopping database initialization
- **Flexibility**: Can be combined with migration control and specific model classes
- **Robust**: Database initialization continues even if auto-relationships fail
**Parameter Priority:**
1. Explicit `has_auto_relationships` parameter (if provided)
2. Auto-detection of auto-relationships availability (default)
3. Fallback to available functionality
## Documentation
For more detailed documentation, please visit the [GitHub repository](https://github.com/puntorigen/easy-model) or refer to the [DOCS.md](https://github.com/puntorigen/easy-model/blob/master/DOCS.md) file.
## License
This project is licensed under the MIT License - see the LICENSE file for details.
Raw data
{
"_id": null,
"home_page": "https://github.com/puntorigen/easy-model",
"name": "async-easy-model",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "orm, sqlmodel, database, async, postgresql, sqlite, mysql",
"author": "Pablo Schaffner",
"author_email": "pablo@puntorigen.com",
"download_url": "https://files.pythonhosted.org/packages/29/80/e47bbfc7dfe6110ca41c2b034933f7efc75c06f39183bae95bf85a463d57/async_easy_model-0.4.2.tar.gz",
"platform": null,
"description": "# async-easy-model\n\nA simplified SQLModel-based ORM for async database operations in Python. async-easy-model provides a clean, intuitive interface for common database operations while leveraging the power of SQLModel and SQLAlchemy.\n\n<p align=\"center\">\n <img src=\"https://img.shields.io/pypi/v/async-easy-model\" alt=\"PyPI Version\">\n <img src=\"https://img.shields.io/pypi/pyversions/async-easy-model\" alt=\"Python Versions\">\n <img src=\"https://img.shields.io/github/license/puntorigen/easy_model\" alt=\"License\">\n</p>\n\n## Features\n\n- \ud83d\ude80 Easy-to-use async database operations with standardized methods\n- \ud83d\udd04 Intuitive APIs with sensible defaults for rapid development\n- \ud83d\udcca Dictionary-based CRUD operations (select, insert, update, delete)\n- \ud83d\udd17 Enhanced relationship handling with eager loading and nested operations\n- \ud83d\udd0d Powerful query methods with flexible ordering support\n- \u2699\ufe0f Automatic relationship detection and bidirectional setup\n- \ud83d\udcf1 Support for PostgreSQL, SQLite, and MySQL databases\n- \ud83d\udee0\ufe0f Built on top of SQLModel and SQLAlchemy for robust performance\n- \ud83d\udcdd Type hints for better IDE support\n- \ud83d\udd52 Automatic `id`, `created_at` and `updated_at` fields provided by default\n- \u23f0 **PostgreSQL DateTime Compatibility**: Automatic timezone-aware to timezone-naive datetime conversion for PostgreSQL TIMESTAMP WITHOUT TIME ZONE columns\n- \ud83d\udd04 Automatic schema migrations for evolving database models\n- \ud83d\udcca Visualization of database schema using Mermaid ER diagrams\n- \ud83d\udccb **JSON Column Support**: Native support for JSON columns with proper serialization in migrations\n\n## Installation\n\n```bash\npip install async-easy-model\n```\n\n## Basic Usage\n\n```python\nfrom async_easy_model import EasyModel, init_db, db_config, Field\nfrom typing import Optional\nfrom datetime import datetime\n\n# Configure your database\ndb_config.configure_sqlite(\"database.db\")\n\n# Define your model\nclass User(EasyModel, table=True):\n #no need to specify id, created_at or updated_at since EasyModel provides them by default\n username: str = Field(unique=True)\n email: str\n\n# Initialize your database\nasync def setup():\n await init_db()\n\n# Use it in your async code\nasync def main():\n await setup()\n # Create a new user\n user = await User.insert({\n \"username\": \"john_doe\",\n \"email\": \"john@example.com\"\n })\n \n # Get user ID\n print(f\"New user id: {user.id}\")\n```\n\n## CRUD Operations\n\nFirst, let's define some models that we'll use throughout the examples:\n\n```python\nfrom async_easy_model import EasyModel, Field\nfrom typing import Optional, List\nfrom datetime import datetime\n\nclass User(EasyModel, table=True):\n username: str = Field(unique=True)\n email: str\n is_active: bool = Field(default=True)\n \nclass Post(EasyModel, table=True):\n title: str\n content: str\n user_id: Optional[int] = Field(default=None, foreign_key=\"user.id\")\n \nclass Comment(EasyModel, table=True):\n text: str\n post_id: Optional[int] = Field(default=None, foreign_key=\"post.id\")\n user_id: Optional[int] = Field(default=None, foreign_key=\"user.id\")\n \nclass Department(EasyModel, table=True):\n name: str = Field(unique=True)\n \nclass Product(EasyModel, table=True):\n name: str\n price: float\n sales: int = Field(default=0)\n \nclass Book(EasyModel, table=True):\n title: str\n author_id: Optional[int] = Field(default=None, foreign_key=\"author.id\")\n \nclass Author(EasyModel, table=True):\n name: str\n```\n\n### Create (Insert)\n\n```python\n# Insert a single record\nuser = await User.insert({\n \"username\": \"john_doe\",\n \"email\": \"john@example.com\"\n})\n\n# Insert multiple records\nusers = await User.insert([\n {\"username\": \"user1\", \"email\": \"user1@example.com\"},\n {\"username\": \"user2\", \"email\": \"user2@example.com\"}\n])\n\n# Insert with nested relationships\nnew_post = await Post.insert({\n \"title\": \"My Post\",\n \"content\": \"Content here\",\n \"user\": {\"username\": \"jane_doe\"}, # Will automatically link to existing user\n \"comments\": [ # Create multiple comments in a single transaction\n {\"text\": \"Great post!\", \"user\": {\"username\": \"reader1\"}},\n {\"text\": \"Thanks for sharing\", \"user\": {\"username\": \"reader2\"}}\n ]\n})\n# Access nested data without requerying\nprint(f\"Post by {new_post.user.username} with {len(new_post.comments)} comments\")\n\n# Insert with nested one-to-many relationships \npublisher = await Publisher.insert({\n \"name\": \"Example Publisher\",\n \"books\": [ # List of nested objects\n {\n \"title\": \"Python Mastery\",\n \"genres\": [\n {\"name\": \"Programming\"},\n {\"name\": \"Education\"}\n ]\n },\n {\"title\": \"Data Science Handbook\"}\n ]\n})\n# Access nested relationships immediately\nprint(f\"Publisher: {publisher.name} with {len(publisher.books)} books\")\nprint(f\"First book genres: {[g.name for g in publisher.books[0].genres]}\")\n```\n\n### Read (Retrieve)\n\n```python\n# Select by ID\nuser = await User.select({\"id\": 1})\n\n# Select with criteria\nusers = await User.select({\"is_active\": True}, all=True)\n\n# Select first matching record\nfirst_user = await User.select({\"is_active\": True}, first=True)\n\n# Select all records\nall_users = await User.select({}, all=True)\n\n# Select with wildcard pattern matching\ngmail_users = await User.select({\"email\": \"*@gmail.com\"}, all=True)\n\n# Select with ordering\nrecent_users = await User.select({}, order_by=\"-created_at\", all=True)\n\n# Select with limit\nlatest_posts = await Post.select({}, order_by=\"-created_at\", limit=5)\n# Note: limit > 1 automatically sets all=True\n\n# Select with multiple ordering fields\nsorted_users = await User.select({}, order_by=[\"last_name\", \"first_name\"], all=True)\n\n# Select with relationship ordering\nposts_by_author = await Post.select({}, order_by=\"user.username\", all=True)\n```\n\n### Update\n\n```python\n# Update by ID\nuser = await User.update({\"is_active\": False}, 1)\n\n# Update by criteria\ncount = await User.update(\n {\"is_active\": False},\n {\"last_login\": None} # Set all users without login to inactive\n)\n\n# Update with relationships\nawait User.update(\n {\"department\": {\"name\": \"Sales\"}}, # Update department relationship\n {\"username\": \"john_doe\"}\n)\n```\n\n### Delete\n\n```python\n# Delete by ID\nsuccess = await User.delete(1)\n\n# Delete by criteria\ndeleted_count = await User.delete({\"is_active\": False})\n\n# Delete with compound criteria\nawait Post.delete({\"user\": {\"username\": \"john_doe\"}, \"is_published\": False})\n```\n\n## Database Schema Visualization\n\nThe package includes a `ModelVisualizer` class that makes it easy to generate Entity-Relationship (ER) diagrams for your database models using Mermaid syntax.\n\n```python\nfrom async_easy_model import EasyModel, init_db, db_config, ModelVisualizer\n\n# Initialize your models and database\nawait init_db()\n\n# Create a visualizer\nvisualizer = ModelVisualizer()\n\n# Generate a Mermaid ER diagram\ner_diagram = visualizer.mermaid()\nprint(er_diagram)\n\n# Generate a shareable link to view the diagram online\ner_link = visualizer.mermaid_link()\nprint(er_link)\n\n# Customize the diagram title\nvisualizer.set_title(\"My Project Database Schema\")\ncustom_diagram = visualizer.mermaid()\n```\n\n### Example Mermaid ER Diagram Output\n\n```mermaid\n---\ntitle: EasyModel Table Schemas\nconfig:\n layout: elk\n---\nerDiagram\n author {\n number id PK\n string name \"required\"\n string email\n }\n book {\n number id PK\n string title \"required\"\n number author_id FK\n string isbn\n number published_year\n author author \"virtual\"\n tag[] tags \"virtual\"\n }\n tag {\n number id PK\n string name \"required\"\n book[] books \"virtual\"\n }\n book_tag {\n number id PK\n number book_id FK \"required\"\n number tag_id FK \"required\"\n book book \"virtual\"\n tag tag \"virtual\"\n }\n review {\n number id PK\n number book_id FK \"required\"\n number rating \"required\"\n string comment\n string reviewer_name \"required\"\n book book \"virtual\"\n }\n book ||--o{ author : \"author_id\"\n book_tag ||--o{ book : \"book_id\"\n book_tag ||--o{ tag : \"tag_id\"\n book }o--o{ tag : \"many-to-many\"\n review ||--o{ book : \"book_id\"\n```\n\nThe diagram automatically:\n- Shows all tables with their fields and data types\n- Identifies primary keys (PK) and foreign keys (FK)\n- Shows required fields and virtual relationships\n- Visualizes relationships between tables with proper cardinality\n- Properly handles many-to-many relationships\n\n## Convenient Query Methods\n\nasync-easy-model provides simplified methods for common query patterns:\n\n```python\n# Get all records with relationships loaded (default)\nusers = await User.all()\n\n# Get all records ordered by a field (ascending)\nusers = await User.all(order_by=\"username\")\n\n# Get all records ordered by a field (descending)\nnewest_users = await User.all(order_by=\"-created_at\")\n\n# Get all records ordered by multiple fields\nsorted_users = await User.all(order_by=[\"last_name\", \"first_name\"])\n\n# Get all records ordered by relationship fields\nbooks = await Book.all(order_by=\"author.name\")\n\n# Get the first record \nuser = await User.first()\n\n# Get the most recently created user\nnewest_user = await User.first(order_by=\"-created_at\")\n\n# Get a limited number of records\nrecent_users = await User.limit(10)\n\n# Get a limited number of records with ordering\ntop_products = await Product.limit(5, order_by=\"-sales\")\n```\n\n## Enhanced Relationship Handling\n\nUsing the models defined earlier, here's how to work with relationships:\n\n```python\n# Load all relationships automatically\npost = await Post.select({\"id\": 1})\nprint(post.user.username) # Access related objects directly\n\n# Load specific relationships\npost = await Post.get_with_related(1, [\"user\", \"comments\"])\n\n# Load relationships after fetching\npost = await Post.select({\"id\": 1}, include_relationships=False)\nawait post.load_related([\"user\", \"comments\"])\n\n# Insert with nested relationships\nnew_post = await Post.insert({\n \"title\": \"My Post\",\n \"content\": \"Content here\",\n \"user\": {\"username\": \"jane_doe\"}, # Will automatically link to existing user\n \"comments\": [ # Create multiple comments in a single transaction\n {\"text\": \"Great post!\", \"user\": {\"username\": \"reader1\"}},\n {\"text\": \"Thanks for sharing\", \"user\": {\"username\": \"reader2\"}}\n ]\n})\n# Access nested data without requerying\nprint(f\"Post by {new_post.user.username} with {len(new_post.comments)} comments\")\n\n# Convert to dictionary with nested relationships\npost_dict = post.to_dict(include_relationships=True, max_depth=2)\n```\n\n## Automatic Relationship Detection\n\nThe package can automatically detect and set up bidirectional relationships between models:\n\n```python\nclass User(EasyModel, table=True):\n username: str\n\nclass Post(EasyModel, table=True):\n title: str\n user_id: int = Field(foreign_key=\"user.id\")\n\n# After init_db():\n# - post.user relationship is automatically available\n# - user.posts relationship is automatically available\n```\n\n## Database Configuration\n\n```python\n# SQLite Configuration\ndb_config.configure_sqlite(\"database.db\")\ndb_config.configure_sqlite(\":memory:\") # In-memory database\n\n# PostgreSQL Configuration\ndb_config.configure_postgres(\n user=\"your_user\",\n password=\"your_password\",\n host=\"localhost\",\n port=\"5432\",\n database=\"your_database\"\n)\n\n# MySQL Configuration\ndb_config.configure_mysql(\n user=\"your_user\",\n password=\"your_password\",\n host=\"localhost\",\n port=\"3306\",\n database=\"your_database\"\n)\n\n# Custom Connection URL\ndb_config.set_connection_url(\"postgresql+asyncpg://user:password@localhost:5432/database\")\n```\n\n### Configurable Default for Relationship Loading\n\n**New in v0.3.9**: You can now set a global default for `include_relationships` behavior across all query methods:\n\n```python\n# Configure with default_include_relationships=False for better performance\ndb_config.configure_sqlite(\"database.db\", default_include_relationships=False)\n\n# Or for PostgreSQL\ndb_config.configure_postgres(\n user=\"your_user\",\n password=\"your_password\",\n host=\"localhost\",\n port=\"5432\",\n database=\"your_database\",\n default_include_relationships=False # Set global default\n)\n\n# Or for MySQL\ndb_config.configure_mysql(\n user=\"your_user\",\n password=\"your_password\",\n host=\"localhost\",\n port=\"3306\",\n database=\"your_database\",\n default_include_relationships=False # Set global default\n)\n```\n\n**Benefits:**\n- **Performance**: Set `default_include_relationships=False` to avoid loading relationships by default, improving query performance\n- **Flexibility**: Still override per method call with explicit `True` or `False` values\n- **Backward Compatible**: Defaults to `True` if not specified, maintaining existing behavior\n\n**Usage Examples:**\n```python\n# With default_include_relationships=False configured:\nusers = await User.all() # No relationships loaded (faster)\nusers_with_rels = await User.all(include_relationships=True) # Override to load relationships\n\n# With default_include_relationships=True configured (default behavior):\nusers = await User.all() # Relationships loaded\nusers_no_rels = await User.all(include_relationships=False) # Override to skip relationships\n```\n\n### Database Initialization Options\n\n**New in v0.4.1**: The `init_db()` function now supports configurable auto-relationships handling:\n\n```python\n# Default behavior (auto-detect auto-relationships availability)\nawait init_db()\n\n# Force enable auto-relationships (will warn if not available)\nawait init_db(has_auto_relationships=True)\n\n# Force disable auto-relationships\nawait init_db(has_auto_relationships=False)\n\n# Combined with other parameters\nawait init_db(\n migrate=True,\n model_classes=[User, Post, Comment],\n has_auto_relationships=False\n)\n```\n\n**Benefits:**\n- **Control**: Explicitly enable or disable auto-relationships functionality\n- **Reliability**: Auto-relationships errors now issue warnings instead of stopping database initialization\n- **Flexibility**: Can be combined with migration control and specific model classes\n- **Robust**: Database initialization continues even if auto-relationships fail\n\n**Parameter Priority:**\n1. Explicit `has_auto_relationships` parameter (if provided)\n2. Auto-detection of auto-relationships availability (default)\n3. Fallback to available functionality\n\n## Documentation\n\nFor more detailed documentation, please visit the [GitHub repository](https://github.com/puntorigen/easy-model) or refer to the [DOCS.md](https://github.com/puntorigen/easy-model/blob/master/DOCS.md) file.\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n",
"bugtrack_url": null,
"license": null,
"summary": "A simplified SQLModel-based ORM for async database operations",
"version": "0.4.2",
"project_urls": {
"Homepage": "https://github.com/puntorigen/easy-model"
},
"split_keywords": [
"orm",
" sqlmodel",
" database",
" async",
" postgresql",
" sqlite",
" mysql"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "4d66327e62c54686f99be594b7fabb858e46f85b44ff2a5d6821b1c07382727d",
"md5": "4c1beda77ee0e22e7388d0fe662373ef",
"sha256": "8de20cdf6aeadd84a9cf3c2704a802ed215d07c12598cc528bf462db767100e4"
},
"downloads": -1,
"filename": "async_easy_model-0.4.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4c1beda77ee0e22e7388d0fe662373ef",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 42987,
"upload_time": "2025-07-26T01:47:02",
"upload_time_iso_8601": "2025-07-26T01:47:02.769934Z",
"url": "https://files.pythonhosted.org/packages/4d/66/327e62c54686f99be594b7fabb858e46f85b44ff2a5d6821b1c07382727d/async_easy_model-0.4.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2980e47bbfc7dfe6110ca41c2b034933f7efc75c06f39183bae95bf85a463d57",
"md5": "c63fbb12e7dae38f0382b55742240377",
"sha256": "e1f054728fff4036d3b187e9607582a85826b0f620916b78db700d8cb14be4c1"
},
"downloads": -1,
"filename": "async_easy_model-0.4.2.tar.gz",
"has_sig": false,
"md5_digest": "c63fbb12e7dae38f0382b55742240377",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 46049,
"upload_time": "2025-07-26T01:47:04",
"upload_time_iso_8601": "2025-07-26T01:47:04.339986Z",
"url": "https://files.pythonhosted.org/packages/29/80/e47bbfc7dfe6110ca41c2b034933f7efc75c06f39183bae95bf85a463d57/async_easy_model-0.4.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-26 01:47:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "puntorigen",
"github_project": "easy-model",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "async-easy-model"
}