async-easy-model 0.1.12__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.2
2
+ Name: async-easy-model
3
+ Version: 0.2.2
4
+ Summary: A simplified SQLModel-based ORM for async database operations
5
+ Home-page: https://github.com/puntorigen/easy-model
6
+ Author: Pablo Schaffner
7
+ Author-email: pablo@puntorigen.com
8
+ Keywords: orm,sqlmodel,database,async,postgresql,sqlite
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: sqlmodel>=0.0.8
23
+ Requires-Dist: sqlalchemy>=2.0.0
24
+ Requires-Dist: asyncpg>=0.25.0
25
+ Requires-Dist: aiosqlite>=0.19.0
26
+ Requires-Dist: greenlet>=3.1.1
27
+ Requires-Dist: inflection>=0.5.1
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: keywords
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # async-easy-model
40
+
41
+ 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.
42
+
43
+ <p align="center">
44
+ <img src="https://img.shields.io/pypi/v/async-easy-model" alt="PyPI Version">
45
+ <img src="https://img.shields.io/pypi/pyversions/async-easy-model" alt="Python Versions">
46
+ <img src="https://img.shields.io/github/license/puntorigen/easy_model" alt="License">
47
+ </p>
48
+
49
+ ## Features
50
+
51
+ - 🚀 Easy-to-use async database operations with standardized methods
52
+ - 🔄 Intuitive APIs with sensible defaults for rapid development
53
+ - 📊 Dictionary-based CRUD operations (select, insert, update, delete)
54
+ - 🔗 Enhanced relationship handling with eager loading and nested operations
55
+ - 🔍 Powerful query methods with flexible ordering support
56
+ - ⚙️ Automatic relationship detection and bidirectional setup
57
+ - 📱 Support for both PostgreSQL and SQLite databases
58
+ - 🛠️ Built on top of SQLModel and SQLAlchemy for robust performance
59
+ - 📝 Type hints for better IDE support
60
+ - 🕒 Automatic `id`, `created_at` and `updated_at` fields provided by default
61
+ - 🔄 Automatic schema migrations for evolving database models
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install async-easy-model
67
+ ```
68
+
69
+ ## Basic Usage
70
+
71
+ ```python
72
+ from async_easy_model import EasyModel, init_db, db_config, Field
73
+ from typing import Optional
74
+ from datetime import datetime
75
+
76
+ # Configure your database
77
+ db_config.configure_sqlite("database.db")
78
+
79
+ # Define your model
80
+ class User(EasyModel, table=True):
81
+ #no need to specify id, created_at or updated_at since EasyModel provides them by default
82
+ username: str = Field(unique=True)
83
+ email: str
84
+
85
+ # Initialize your database
86
+ async def setup():
87
+ await init_db()
88
+
89
+ # Use it in your async code
90
+ async def main():
91
+ await setup()
92
+ # Create a new user
93
+ user = await User.insert({
94
+ "username": "john_doe",
95
+ "email": "john@example.com"
96
+ })
97
+
98
+ # Get user ID
99
+ print(f"New user id: {user.id}")
100
+ ```
101
+
102
+ ## CRUD Operations
103
+
104
+ First, let's define some models that we'll use throughout the examples:
105
+
106
+ ```python
107
+ from async_easy_model import EasyModel, Field
108
+ from typing import Optional, List
109
+ from datetime import datetime
110
+
111
+ class User(EasyModel, table=True):
112
+ username: str = Field(unique=True)
113
+ email: str
114
+ is_active: bool = Field(default=True)
115
+
116
+ class Post(EasyModel, table=True):
117
+ title: str
118
+ content: str
119
+ user_id: Optional[int] = Field(default=None, foreign_key="user.id")
120
+
121
+ class Comment(EasyModel, table=True):
122
+ text: str
123
+ post_id: Optional[int] = Field(default=None, foreign_key="post.id")
124
+ user_id: Optional[int] = Field(default=None, foreign_key="user.id")
125
+
126
+ class Department(EasyModel, table=True):
127
+ name: str = Field(unique=True)
128
+
129
+ class Product(EasyModel, table=True):
130
+ name: str
131
+ price: float
132
+ sales: int = Field(default=0)
133
+
134
+ class Book(EasyModel, table=True):
135
+ title: str
136
+ author_id: Optional[int] = Field(default=None, foreign_key="author.id")
137
+
138
+ class Author(EasyModel, table=True):
139
+ name: str
140
+ ```
141
+
142
+ ### Create (Insert)
143
+
144
+ ```python
145
+ # Insert a single record
146
+ user = await User.insert({
147
+ "username": "john_doe",
148
+ "email": "john@example.com"
149
+ })
150
+
151
+ # Insert multiple records
152
+ users = await User.insert([
153
+ {"username": "user1", "email": "user1@example.com"},
154
+ {"username": "user2", "email": "user2@example.com"}
155
+ ])
156
+
157
+ # Insert with relationships using nested dictionaries
158
+ post = await Post.insert({
159
+ "title": "My First Post",
160
+ "content": "Hello world!",
161
+ "user": {"username": "john_doe"} # Will automatically link to existing user
162
+ })
163
+ ```
164
+
165
+ ### Read (Retrieve)
166
+
167
+ ```python
168
+ # Select by ID
169
+ user = await User.select({"id": 1})
170
+
171
+ # Select with criteria
172
+ users = await User.select({"is_active": True}, all=True)
173
+
174
+ # Select first matching record
175
+ first_user = await User.select({"is_active": True}, first=True)
176
+
177
+ # Select all records
178
+ all_users = await User.select({}, all=True)
179
+
180
+ # Select with wildcard pattern matching
181
+ gmail_users = await User.select({"email": "*@gmail.com"}, all=True)
182
+
183
+ # Select with ordering
184
+ recent_users = await User.select({}, order_by="-created_at", all=True)
185
+
186
+ # Select with limit
187
+ latest_posts = await Post.select({}, order_by="-created_at", limit=5)
188
+ # Note: limit > 1 automatically sets all=True
189
+
190
+ # Select with multiple ordering fields
191
+ sorted_users = await User.select({}, order_by=["last_name", "first_name"], all=True)
192
+
193
+ # Select with relationship ordering
194
+ posts_by_author = await Post.select({}, order_by="user.username", all=True)
195
+ ```
196
+
197
+ ### Update
198
+
199
+ ```python
200
+ # Update by ID
201
+ user = await User.update({"is_active": False}, 1)
202
+
203
+ # Update by criteria
204
+ count = await User.update(
205
+ {"is_active": False},
206
+ {"last_login": None} # Set all users without login to inactive
207
+ )
208
+
209
+ # Update with relationships
210
+ await User.update(
211
+ {"department": {"name": "Sales"}}, # Update department relationship
212
+ {"username": "john_doe"}
213
+ )
214
+ ```
215
+
216
+ ### Delete
217
+
218
+ ```python
219
+ # Delete by ID
220
+ success = await User.delete(1)
221
+
222
+ # Delete by criteria
223
+ deleted_count = await User.delete({"is_active": False})
224
+
225
+ # Delete with compound criteria
226
+ await Post.delete({"user": {"username": "john_doe"}, "is_published": False})
227
+ ```
228
+
229
+ ## Convenient Query Methods
230
+
231
+ async-easy-model provides simplified methods for common query patterns:
232
+
233
+ ```python
234
+ # Get all records with relationships loaded (default)
235
+ users = await User.all()
236
+
237
+ # Get all records ordered by a field (ascending)
238
+ users = await User.all(order_by="username")
239
+
240
+ # Get all records ordered by a field (descending)
241
+ newest_users = await User.all(order_by="-created_at")
242
+
243
+ # Get all records ordered by multiple fields
244
+ sorted_users = await User.all(order_by=["last_name", "first_name"])
245
+
246
+ # Get all records ordered by relationship fields
247
+ books = await Book.all(order_by="author.name")
248
+
249
+ # Get the first record
250
+ user = await User.first()
251
+
252
+ # Get the most recently created user
253
+ newest_user = await User.first(order_by="-created_at")
254
+
255
+ # Get a limited number of records
256
+ recent_users = await User.limit(10)
257
+
258
+ # Get a limited number of records with ordering
259
+ top_products = await Product.limit(5, order_by="-sales")
260
+ ```
261
+
262
+ ## Enhanced Relationship Handling
263
+
264
+ Using the models defined earlier, here's how to work with relationships:
265
+
266
+ ```python
267
+ # Load all relationships automatically
268
+ post = await Post.select({"id": 1}, include_relationships=True)
269
+ print(post.user.username) # Access related objects directly
270
+
271
+ # Load specific relationships
272
+ post = await Post.get_with_related(1, ["user", "comments"])
273
+
274
+ # Load relationships after fetching
275
+ post = await Post.select({"id": 1})
276
+ await post.load_related(["user", "comments"])
277
+
278
+ # Insert with related objects in a single transaction
279
+ new_post = await Post.insert_with_related({
280
+ "title": "My Post",
281
+ "content": "Content here",
282
+ "user": {"username": "john_doe"}
283
+ })
284
+
285
+ # Convert to dictionary with nested relationships
286
+ post_dict = post.to_dict(include_relationships=True, max_depth=2)
287
+ ```
288
+
289
+ ## Automatic Relationship Detection
290
+
291
+ The package can automatically detect and set up bidirectional relationships between models:
292
+
293
+ ```python
294
+ class User(EasyModel, table=True):
295
+ username: str
296
+
297
+ class Post(EasyModel, table=True):
298
+ title: str
299
+ user_id: int = Field(foreign_key="user.id")
300
+
301
+ # After init_db():
302
+ # - post.user relationship is automatically available
303
+ # - user.posts relationship is automatically available
304
+ ```
305
+
306
+ ## Database Configuration
307
+
308
+ ```python
309
+ # SQLite Configuration
310
+ db_config.configure_sqlite("database.db")
311
+ db_config.configure_sqlite(":memory:") # In-memory database
312
+
313
+ # PostgreSQL Configuration
314
+ db_config.configure_postgres(
315
+ user="your_user",
316
+ password="your_password",
317
+ host="localhost",
318
+ port="5432",
319
+ database="your_database"
320
+ )
321
+
322
+ # Custom Connection URL
323
+ db_config.set_connection_url("postgresql+asyncpg://user:password@localhost:5432/database")
324
+ ```
325
+
326
+ ## Documentation
327
+
328
+ 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/main/DOCS.md) file.
329
+
330
+ ## License
331
+
332
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,10 @@
1
+ async_easy_model/__init__.py,sha256=A0U-LtrK9tjK-b94oDNJyc3XFmmnjQAHXP7Bs-FHx18,1642
2
+ async_easy_model/auto_relationships.py,sha256=VetxcrOdKGGSImTiysRFR8PSOSlo50RqnVG95CLe8Jg,22433
3
+ async_easy_model/migrations.py,sha256=rYDGCGlruSugAmPfdIF2-uhyG6UvC_2qbF3BXJ084qI,17803
4
+ async_easy_model/model.py,sha256=TUTTLP47YWCdlsrsyf-7HmMAhPdlvTNZG416pA7UfCo,52958
5
+ async_easy_model/relationships.py,sha256=vR5BsJpGaDcecCcNlg9-ouZfxFXFQv5kOyiXhKp_T7A,3286
6
+ async_easy_model-0.2.2.dist-info/LICENSE,sha256=uwDkl6oHbRltW7vYKNc4doJyhtwhyrSNFFlPpKATwLE,1072
7
+ async_easy_model-0.2.2.dist-info/METADATA,sha256=QcX4beoXLL0fbPJ8ZVMAL4Q5QlXGgJfb4jjSjPx3Yqw,9474
8
+ async_easy_model-0.2.2.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
9
+ async_easy_model-0.2.2.dist-info/top_level.txt,sha256=e5_47sGmJnyxz2msfwU6C316EqmrSd9RGIYwZyWx68E,17
10
+ async_easy_model-0.2.2.dist-info/RECORD,,