async-easy-model 0.1.12__tar.gz → 0.2.2__tar.gz

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.
Files changed (20) hide show
  1. async_easy_model-0.2.2/PKG-INFO +332 -0
  2. async_easy_model-0.2.2/README.md +294 -0
  3. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model/__init__.py +1 -1
  4. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model/auto_relationships.py +12 -0
  5. async_easy_model-0.2.2/async_easy_model/model.py +1197 -0
  6. async_easy_model-0.2.2/async_easy_model.egg-info/PKG-INFO +332 -0
  7. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/setup.py +1 -1
  8. async_easy_model-0.1.12/PKG-INFO +0 -533
  9. async_easy_model-0.1.12/README.md +0 -495
  10. async_easy_model-0.1.12/async_easy_model/model.py +0 -646
  11. async_easy_model-0.1.12/async_easy_model.egg-info/PKG-INFO +0 -533
  12. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/LICENSE +0 -0
  13. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model/migrations.py +0 -0
  14. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model/relationships.py +0 -0
  15. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model.egg-info/SOURCES.txt +0 -0
  16. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model.egg-info/dependency_links.txt +0 -0
  17. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model.egg-info/requires.txt +0 -0
  18. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/async_easy_model.egg-info/top_level.txt +0 -0
  19. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/setup.cfg +0 -0
  20. {async_easy_model-0.1.12 → async_easy_model-0.2.2}/tests/test_easy_model.py +0 -0
@@ -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,294 @@
1
+ # async-easy-model
2
+
3
+ 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.
4
+
5
+ <p align="center">
6
+ <img src="https://img.shields.io/pypi/v/async-easy-model" alt="PyPI Version">
7
+ <img src="https://img.shields.io/pypi/pyversions/async-easy-model" alt="Python Versions">
8
+ <img src="https://img.shields.io/github/license/puntorigen/easy_model" alt="License">
9
+ </p>
10
+
11
+ ## Features
12
+
13
+ - 🚀 Easy-to-use async database operations with standardized methods
14
+ - 🔄 Intuitive APIs with sensible defaults for rapid development
15
+ - 📊 Dictionary-based CRUD operations (select, insert, update, delete)
16
+ - 🔗 Enhanced relationship handling with eager loading and nested operations
17
+ - 🔍 Powerful query methods with flexible ordering support
18
+ - ⚙️ Automatic relationship detection and bidirectional setup
19
+ - 📱 Support for both PostgreSQL and SQLite databases
20
+ - 🛠️ Built on top of SQLModel and SQLAlchemy for robust performance
21
+ - 📝 Type hints for better IDE support
22
+ - 🕒 Automatic `id`, `created_at` and `updated_at` fields provided by default
23
+ - 🔄 Automatic schema migrations for evolving database models
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install async-easy-model
29
+ ```
30
+
31
+ ## Basic Usage
32
+
33
+ ```python
34
+ from async_easy_model import EasyModel, init_db, db_config, Field
35
+ from typing import Optional
36
+ from datetime import datetime
37
+
38
+ # Configure your database
39
+ db_config.configure_sqlite("database.db")
40
+
41
+ # Define your model
42
+ class User(EasyModel, table=True):
43
+ #no need to specify id, created_at or updated_at since EasyModel provides them by default
44
+ username: str = Field(unique=True)
45
+ email: str
46
+
47
+ # Initialize your database
48
+ async def setup():
49
+ await init_db()
50
+
51
+ # Use it in your async code
52
+ async def main():
53
+ await setup()
54
+ # Create a new user
55
+ user = await User.insert({
56
+ "username": "john_doe",
57
+ "email": "john@example.com"
58
+ })
59
+
60
+ # Get user ID
61
+ print(f"New user id: {user.id}")
62
+ ```
63
+
64
+ ## CRUD Operations
65
+
66
+ First, let's define some models that we'll use throughout the examples:
67
+
68
+ ```python
69
+ from async_easy_model import EasyModel, Field
70
+ from typing import Optional, List
71
+ from datetime import datetime
72
+
73
+ class User(EasyModel, table=True):
74
+ username: str = Field(unique=True)
75
+ email: str
76
+ is_active: bool = Field(default=True)
77
+
78
+ class Post(EasyModel, table=True):
79
+ title: str
80
+ content: str
81
+ user_id: Optional[int] = Field(default=None, foreign_key="user.id")
82
+
83
+ class Comment(EasyModel, table=True):
84
+ text: str
85
+ post_id: Optional[int] = Field(default=None, foreign_key="post.id")
86
+ user_id: Optional[int] = Field(default=None, foreign_key="user.id")
87
+
88
+ class Department(EasyModel, table=True):
89
+ name: str = Field(unique=True)
90
+
91
+ class Product(EasyModel, table=True):
92
+ name: str
93
+ price: float
94
+ sales: int = Field(default=0)
95
+
96
+ class Book(EasyModel, table=True):
97
+ title: str
98
+ author_id: Optional[int] = Field(default=None, foreign_key="author.id")
99
+
100
+ class Author(EasyModel, table=True):
101
+ name: str
102
+ ```
103
+
104
+ ### Create (Insert)
105
+
106
+ ```python
107
+ # Insert a single record
108
+ user = await User.insert({
109
+ "username": "john_doe",
110
+ "email": "john@example.com"
111
+ })
112
+
113
+ # Insert multiple records
114
+ users = await User.insert([
115
+ {"username": "user1", "email": "user1@example.com"},
116
+ {"username": "user2", "email": "user2@example.com"}
117
+ ])
118
+
119
+ # Insert with relationships using nested dictionaries
120
+ post = await Post.insert({
121
+ "title": "My First Post",
122
+ "content": "Hello world!",
123
+ "user": {"username": "john_doe"} # Will automatically link to existing user
124
+ })
125
+ ```
126
+
127
+ ### Read (Retrieve)
128
+
129
+ ```python
130
+ # Select by ID
131
+ user = await User.select({"id": 1})
132
+
133
+ # Select with criteria
134
+ users = await User.select({"is_active": True}, all=True)
135
+
136
+ # Select first matching record
137
+ first_user = await User.select({"is_active": True}, first=True)
138
+
139
+ # Select all records
140
+ all_users = await User.select({}, all=True)
141
+
142
+ # Select with wildcard pattern matching
143
+ gmail_users = await User.select({"email": "*@gmail.com"}, all=True)
144
+
145
+ # Select with ordering
146
+ recent_users = await User.select({}, order_by="-created_at", all=True)
147
+
148
+ # Select with limit
149
+ latest_posts = await Post.select({}, order_by="-created_at", limit=5)
150
+ # Note: limit > 1 automatically sets all=True
151
+
152
+ # Select with multiple ordering fields
153
+ sorted_users = await User.select({}, order_by=["last_name", "first_name"], all=True)
154
+
155
+ # Select with relationship ordering
156
+ posts_by_author = await Post.select({}, order_by="user.username", all=True)
157
+ ```
158
+
159
+ ### Update
160
+
161
+ ```python
162
+ # Update by ID
163
+ user = await User.update({"is_active": False}, 1)
164
+
165
+ # Update by criteria
166
+ count = await User.update(
167
+ {"is_active": False},
168
+ {"last_login": None} # Set all users without login to inactive
169
+ )
170
+
171
+ # Update with relationships
172
+ await User.update(
173
+ {"department": {"name": "Sales"}}, # Update department relationship
174
+ {"username": "john_doe"}
175
+ )
176
+ ```
177
+
178
+ ### Delete
179
+
180
+ ```python
181
+ # Delete by ID
182
+ success = await User.delete(1)
183
+
184
+ # Delete by criteria
185
+ deleted_count = await User.delete({"is_active": False})
186
+
187
+ # Delete with compound criteria
188
+ await Post.delete({"user": {"username": "john_doe"}, "is_published": False})
189
+ ```
190
+
191
+ ## Convenient Query Methods
192
+
193
+ async-easy-model provides simplified methods for common query patterns:
194
+
195
+ ```python
196
+ # Get all records with relationships loaded (default)
197
+ users = await User.all()
198
+
199
+ # Get all records ordered by a field (ascending)
200
+ users = await User.all(order_by="username")
201
+
202
+ # Get all records ordered by a field (descending)
203
+ newest_users = await User.all(order_by="-created_at")
204
+
205
+ # Get all records ordered by multiple fields
206
+ sorted_users = await User.all(order_by=["last_name", "first_name"])
207
+
208
+ # Get all records ordered by relationship fields
209
+ books = await Book.all(order_by="author.name")
210
+
211
+ # Get the first record
212
+ user = await User.first()
213
+
214
+ # Get the most recently created user
215
+ newest_user = await User.first(order_by="-created_at")
216
+
217
+ # Get a limited number of records
218
+ recent_users = await User.limit(10)
219
+
220
+ # Get a limited number of records with ordering
221
+ top_products = await Product.limit(5, order_by="-sales")
222
+ ```
223
+
224
+ ## Enhanced Relationship Handling
225
+
226
+ Using the models defined earlier, here's how to work with relationships:
227
+
228
+ ```python
229
+ # Load all relationships automatically
230
+ post = await Post.select({"id": 1}, include_relationships=True)
231
+ print(post.user.username) # Access related objects directly
232
+
233
+ # Load specific relationships
234
+ post = await Post.get_with_related(1, ["user", "comments"])
235
+
236
+ # Load relationships after fetching
237
+ post = await Post.select({"id": 1})
238
+ await post.load_related(["user", "comments"])
239
+
240
+ # Insert with related objects in a single transaction
241
+ new_post = await Post.insert_with_related({
242
+ "title": "My Post",
243
+ "content": "Content here",
244
+ "user": {"username": "john_doe"}
245
+ })
246
+
247
+ # Convert to dictionary with nested relationships
248
+ post_dict = post.to_dict(include_relationships=True, max_depth=2)
249
+ ```
250
+
251
+ ## Automatic Relationship Detection
252
+
253
+ The package can automatically detect and set up bidirectional relationships between models:
254
+
255
+ ```python
256
+ class User(EasyModel, table=True):
257
+ username: str
258
+
259
+ class Post(EasyModel, table=True):
260
+ title: str
261
+ user_id: int = Field(foreign_key="user.id")
262
+
263
+ # After init_db():
264
+ # - post.user relationship is automatically available
265
+ # - user.posts relationship is automatically available
266
+ ```
267
+
268
+ ## Database Configuration
269
+
270
+ ```python
271
+ # SQLite Configuration
272
+ db_config.configure_sqlite("database.db")
273
+ db_config.configure_sqlite(":memory:") # In-memory database
274
+
275
+ # PostgreSQL Configuration
276
+ db_config.configure_postgres(
277
+ user="your_user",
278
+ password="your_password",
279
+ host="localhost",
280
+ port="5432",
281
+ database="your_database"
282
+ )
283
+
284
+ # Custom Connection URL
285
+ db_config.set_connection_url("postgresql+asyncpg://user:password@localhost:5432/database")
286
+ ```
287
+
288
+ ## Documentation
289
+
290
+ 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.
291
+
292
+ ## License
293
+
294
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -6,7 +6,7 @@ from typing import Optional, Any
6
6
  from .model import EasyModel, init_db, db_config
7
7
  from sqlmodel import Field, Relationship as SQLModelRelationship
8
8
 
9
- __version__ = "0.1.12"
9
+ __version__ = "0.2.2"
10
10
  __all__ = ["EasyModel", "init_db", "db_config", "Field", "Relationship", "Relation", "enable_auto_relationships", "disable_auto_relationships", "process_auto_relationships", "MigrationManager", "check_and_migrate_models"]
11
11
 
12
12
  # Create a more user-friendly Relationship function
@@ -22,8 +22,20 @@ _auto_processed_models = set()
22
22
  _auto_relationships_processed = False
23
23
 
24
24
  # Flag to enable/disable automatic relationship detection
25
+ # Disabled by default and will be enabled during init_db
25
26
  _auto_relationships_enabled = False
26
27
 
28
+ # Automatically disable auto-relationships at module import time
29
+ # This ensures models can be defined without immediate processing
30
+ def _disable_auto_relationships_on_import():
31
+ # This will be automatically called when the module is imported
32
+ global _auto_relationships_enabled
33
+ _auto_relationships_enabled = False
34
+ logger.info("Auto relationships disabled by default at import")
35
+
36
+ # Call the function immediately when this module is imported
37
+ _disable_auto_relationships_on_import()
38
+
27
39
  def pluralize_name(name: str) -> str:
28
40
  """Convert a singular noun to its plural form."""
29
41
  # Check if the name already ends with 's'