async-easy-model 0.2.9__py3-none-any.whl → 0.3.0__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.
@@ -12,7 +12,7 @@ from typing import Optional, Any
12
12
  from .model import EasyModel, init_db, db_config
13
13
  from sqlmodel import Field, Relationship as SQLModelRelationship
14
14
 
15
- __version__ = "0.2.9"
15
+ __version__ = "0.3.0"
16
16
  __all__ = ["EasyModel", "init_db", "db_config", "Field", "Relationship", "Relation", "enable_auto_relationships", "disable_auto_relationships", "process_auto_relationships", "MigrationManager", "check_and_migrate_models", "ModelVisualizer"]
17
17
 
18
18
  # Create a more user-friendly Relationship function
async_easy_model/model.py CHANGED
@@ -14,6 +14,9 @@ import re
14
14
 
15
15
  T = TypeVar("T", bound="EasyModel")
16
16
 
17
+ # Global database configuration instance (forward declaration)
18
+ db_config = None
19
+
17
20
  class DatabaseConfig:
18
21
  _engine = None
19
22
  _session_maker = None
@@ -107,13 +110,74 @@ class DatabaseConfig:
107
110
  # Global database configuration instance.
108
111
  db_config = DatabaseConfig()
109
112
 
113
+ def _normalize_datetime_for_db(value: Any) -> Any:
114
+ """
115
+ Normalize datetime values for database compatibility.
116
+
117
+ For PostgreSQL with TIMESTAMP WITHOUT TIME ZONE columns, converts timezone-aware
118
+ datetimes to timezone-naive UTC datetimes. For other databases, returns the value unchanged.
119
+
120
+ Args:
121
+ value: The value to potentially normalize
122
+
123
+ Returns:
124
+ The normalized value
125
+ """
126
+ global db_config
127
+
128
+ # Only process datetime objects
129
+ if not isinstance(value, datetime):
130
+ return value
131
+
132
+ # Only normalize for PostgreSQL
133
+ if db_config and db_config.db_type == "postgresql":
134
+ # If the datetime is timezone-aware, convert to UTC and make naive
135
+ if value.tzinfo is not None:
136
+ return value.astimezone(tz.utc).replace(tzinfo=None)
137
+
138
+ return value
139
+
140
+ def _normalize_data_for_db(data: Dict[str, Any]) -> Dict[str, Any]:
141
+ """
142
+ Normalize all datetime values in a data dictionary for database compatibility.
143
+
144
+ Args:
145
+ data: Dictionary containing field values
146
+
147
+ Returns:
148
+ Dictionary with normalized datetime values
149
+ """
150
+ normalized_data = {}
151
+ for key, value in data.items():
152
+ normalized_data[key] = _normalize_datetime_for_db(value)
153
+ return normalized_data
154
+
155
+ def _get_normalized_datetime() -> datetime:
156
+ """
157
+ Get a datetime that's appropriate for the current database backend.
158
+
159
+ Returns:
160
+ For PostgreSQL: timezone-naive UTC datetime
161
+ For SQLite: timezone-aware UTC datetime (for backward compatibility)
162
+ """
163
+ global db_config
164
+
165
+ utc_now = datetime.now(tz.utc)
166
+
167
+ # For PostgreSQL, return timezone-naive datetime
168
+ if db_config and db_config.db_type == "postgresql":
169
+ return utc_now.replace(tzinfo=None)
170
+
171
+ # For SQLite and others, return timezone-aware datetime (backward compatibility)
172
+ return utc_now
173
+
110
174
  class EasyModel(SQLModel):
111
175
  """
112
176
  Base model class providing common async database operations.
113
177
  """
114
178
  id: Optional[int] = Field(default=None, primary_key=True)
115
- created_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(tz.utc))
116
- updated_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(tz.utc))
179
+ created_at: Optional[datetime] = Field(default_factory=_get_normalized_datetime)
180
+ updated_at: Optional[datetime] = Field(default_factory=_get_normalized_datetime)
117
181
 
118
182
  # Default table args with extend_existing=True to ensure all subclasses can redefine tables
119
183
  __table_args__ = {"extend_existing": True}
@@ -437,6 +501,9 @@ class EasyModel(SQLModel):
437
501
  try:
438
502
  processed_data = await cls._process_relationships_for_insert(session, data)
439
503
 
504
+ # Normalize datetime values for database compatibility
505
+ processed_data = _normalize_data_for_db(processed_data)
506
+
440
507
  # Create the model instance
441
508
  obj = cls(**processed_data)
442
509
  session.add(obj)
@@ -769,9 +836,10 @@ class EasyModel(SQLModel):
769
836
  if existing:
770
837
  raise ValueError(f"Cannot update {field_name} to '{new_value}': value already exists")
771
838
 
772
- # Apply the updates
839
+ # Apply the updates with datetime normalization
773
840
  for key, value in data.items():
774
- setattr(record, key, value)
841
+ normalized_value = _normalize_datetime_for_db(value)
842
+ setattr(record, key, normalized_value)
775
843
 
776
844
  # Process many-to-many relationships if any
777
845
  for rel_name, rel_data in many_to_many_data.items():
@@ -1383,7 +1451,7 @@ class EasyModel(SQLModel):
1383
1451
  def _update_updated_at(session, flush_context, instances):
1384
1452
  for instance in session.dirty:
1385
1453
  if isinstance(instance, EasyModel) and hasattr(instance, "updated_at"):
1386
- instance.updated_at = datetime.now(tz.utc)
1454
+ instance.updated_at = _get_normalized_datetime()
1387
1455
 
1388
1456
  async def init_db(migrate: bool = True, model_classes: List[Type[SQLModel]] = None):
1389
1457
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async-easy-model
3
- Version: 0.2.9
3
+ Version: 0.3.0
4
4
  Summary: A simplified SQLModel-based ORM for async database operations
5
5
  Home-page: https://github.com/puntorigen/easy-model
6
6
  Author: Pablo Schaffner
@@ -59,6 +59,7 @@ A simplified SQLModel-based ORM for async database operations in Python. async-e
59
59
  - 🛠️ Built on top of SQLModel and SQLAlchemy for robust performance
60
60
  - 📝 Type hints for better IDE support
61
61
  - 🕒 Automatic `id`, `created_at` and `updated_at` fields provided by default
62
+ - ⏰ **PostgreSQL DateTime Compatibility**: Automatic timezone-aware to timezone-naive datetime conversion for PostgreSQL TIMESTAMP WITHOUT TIME ZONE columns
62
63
  - 🔄 Automatic schema migrations for evolving database models
63
64
  - 📊 Visualization of database schema using Mermaid ER diagrams
64
65
 
@@ -0,0 +1,11 @@
1
+ async_easy_model/__init__.py,sha256=IXheLaQx-dpGPL6oBEaaIftZDihlFB7bwzEWfMlZ4Ts,1921
2
+ async_easy_model/auto_relationships.py,sha256=V2LAzNi7y-keFk4C_m-byVRM-k_7nL5HEy9Ig3nEdq8,27756
3
+ async_easy_model/migrations.py,sha256=rYDGCGlruSugAmPfdIF2-uhyG6UvC_2qbF3BXJ084qI,17803
4
+ async_easy_model/model.py,sha256=XxlFOYiX8HT1B6PBB3RubuvB5j4aYKEyO-sPA202Y2U,66508
5
+ async_easy_model/relationships.py,sha256=vR5BsJpGaDcecCcNlg9-ouZfxFXFQv5kOyiXhKp_T7A,3286
6
+ async_easy_model/visualization.py,sha256=RVCdc8j3uUQe-zy3jXju_yhA13qJ8KWVbQ5fQyjyqkA,29973
7
+ async_easy_model-0.3.0.dist-info/licenses/LICENSE,sha256=uwDkl6oHbRltW7vYKNc4doJyhtwhyrSNFFlPpKATwLE,1072
8
+ async_easy_model-0.3.0.dist-info/METADATA,sha256=0PEppZL9WiSl1uKDqlC-f-l9rrrM-x5HVcRWBcRgsJU,13047
9
+ async_easy_model-0.3.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
10
+ async_easy_model-0.3.0.dist-info/top_level.txt,sha256=e5_47sGmJnyxz2msfwU6C316EqmrSd9RGIYwZyWx68E,17
11
+ async_easy_model-0.3.0.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- async_easy_model/__init__.py,sha256=eGTq3OypupyVvLe2Zyn3ZFBeuU4v8EXW-XQ047vUMoM,1921
2
- async_easy_model/auto_relationships.py,sha256=V2LAzNi7y-keFk4C_m-byVRM-k_7nL5HEy9Ig3nEdq8,27756
3
- async_easy_model/migrations.py,sha256=rYDGCGlruSugAmPfdIF2-uhyG6UvC_2qbF3BXJ084qI,17803
4
- async_easy_model/model.py,sha256=pBWRAVhazZuI_rBOnILPttiCF_ZEomWBJGDmfdFp8Nk,64243
5
- async_easy_model/relationships.py,sha256=vR5BsJpGaDcecCcNlg9-ouZfxFXFQv5kOyiXhKp_T7A,3286
6
- async_easy_model/visualization.py,sha256=RVCdc8j3uUQe-zy3jXju_yhA13qJ8KWVbQ5fQyjyqkA,29973
7
- async_easy_model-0.2.9.dist-info/licenses/LICENSE,sha256=uwDkl6oHbRltW7vYKNc4doJyhtwhyrSNFFlPpKATwLE,1072
8
- async_easy_model-0.2.9.dist-info/METADATA,sha256=iVRM1qe6DfdFUSa5FYYES-oXgkKScm7m2BVClhlH-k8,12888
9
- async_easy_model-0.2.9.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
10
- async_easy_model-0.2.9.dist-info/top_level.txt,sha256=e5_47sGmJnyxz2msfwU6C316EqmrSd9RGIYwZyWx68E,17
11
- async_easy_model-0.2.9.dist-info/RECORD,,