async-easy-model 0.2.2__py3-none-any.whl → 0.2.3__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.
@@ -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.2.2"
9
+ __version__ = "0.2.3"
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
async_easy_model/model.py CHANGED
@@ -411,6 +411,13 @@ class EasyModel(SQLModel):
411
411
  # Process relationships first
412
412
  processed_item = await cls._process_relationships_for_insert(session, item)
413
413
 
414
+ # Extract special _related_* fields for post-processing
415
+ related_fields = {}
416
+ for key in list(processed_item.keys()):
417
+ if key.startswith("_related_"):
418
+ rel_name = key[9:] # Remove "_related_" prefix
419
+ related_fields[rel_name] = processed_item.pop(key)
420
+
414
421
  # Check if a record with unique constraints already exists
415
422
  unique_fields = cls._get_unique_fields()
416
423
  existing_obj = None
@@ -433,12 +440,33 @@ class EasyModel(SQLModel):
433
440
  for key, value in processed_item.items():
434
441
  if key != 'id': # Don't update ID
435
442
  setattr(existing_obj, key, value)
436
- objects.append(existing_obj)
443
+ obj = existing_obj
437
444
  else:
438
445
  # Create new object
439
446
  obj = cls(**processed_item)
440
447
  session.add(obj)
441
- objects.append(obj)
448
+
449
+ # Flush to get the ID for this object
450
+ await session.flush()
451
+
452
+ # Now handle any one-to-many relationships
453
+ for rel_name, related_objects in related_fields.items():
454
+ # Check if the relationship attribute exists in the class (not the instance)
455
+ if hasattr(cls, rel_name):
456
+ # Get the relationship attribute from the class
457
+ rel_attr = getattr(cls, rel_name)
458
+
459
+ # Check if it's a SQLAlchemy relationship
460
+ if hasattr(rel_attr, 'property') and hasattr(rel_attr.property, 'back_populates'):
461
+ back_attr = rel_attr.property.back_populates
462
+
463
+ # For each related object, set the back reference to this object
464
+ for related_obj in related_objects:
465
+ setattr(related_obj, back_attr, obj)
466
+ # Make sure the related object is in the session
467
+ session.add(related_obj)
468
+
469
+ objects.append(obj)
442
470
  except Exception as e:
443
471
  logging.error(f"Error inserting record: {e}")
444
472
  await session.rollback()
@@ -465,6 +493,13 @@ class EasyModel(SQLModel):
465
493
  # Process relationships first
466
494
  processed_data = await cls._process_relationships_for_insert(session, data)
467
495
 
496
+ # Extract special _related_* fields for post-processing
497
+ related_fields = {}
498
+ for key in list(processed_data.keys()):
499
+ if key.startswith("_related_"):
500
+ rel_name = key[9:] # Remove "_related_" prefix
501
+ related_fields[rel_name] = processed_data.pop(key)
502
+
468
503
  # Check if a record with unique constraints already exists
469
504
  unique_fields = cls._get_unique_fields()
470
505
  existing_obj = None
@@ -494,6 +529,24 @@ class EasyModel(SQLModel):
494
529
  session.add(obj)
495
530
 
496
531
  await session.flush() # Flush to get the ID
532
+
533
+ # Now handle any one-to-many relationships
534
+ for rel_name, related_objects in related_fields.items():
535
+ # Check if the relationship attribute exists in the class (not the instance)
536
+ if hasattr(cls, rel_name):
537
+ # Get the relationship attribute from the class
538
+ rel_attr = getattr(cls, rel_name)
539
+
540
+ # Check if it's a SQLAlchemy relationship
541
+ if hasattr(rel_attr, 'property') and hasattr(rel_attr.property, 'back_populates'):
542
+ back_attr = rel_attr.property.back_populates
543
+
544
+ # For each related object, set the back reference to this object
545
+ for related_obj in related_objects:
546
+ setattr(related_obj, back_attr, obj)
547
+ # Make sure the related object is in the session
548
+ session.add(related_obj)
549
+
497
550
  await session.commit()
498
551
 
499
552
  if include_relationships:
@@ -510,60 +563,6 @@ class EasyModel(SQLModel):
510
563
  logging.error(f"Error inserting record: {e}")
511
564
  await session.rollback()
512
565
  raise
513
-
514
- @classmethod
515
- async def insert_with_related(
516
- cls: Type[T],
517
- data: Dict[str, Any],
518
- related_data: Dict[str, List[Dict[str, Any]]] = None
519
- ) -> T:
520
- """
521
- Create a model instance with related objects in a single transaction.
522
-
523
- Args:
524
- data: Dictionary of field values for the main model
525
- related_data: Dictionary mapping relationship names to lists of data dictionaries
526
- for creating related objects
527
-
528
- Returns:
529
- The created model instance with relationships loaded
530
- """
531
- if related_data is None:
532
- related_data = {}
533
-
534
- async with cls.get_session() as session:
535
- # Create the main object
536
- obj = cls(**data)
537
- session.add(obj)
538
- await session.flush() # Flush to get the ID
539
-
540
- # Create related objects
541
- for rel_name, items_data in related_data.items():
542
- if not hasattr(cls, rel_name):
543
- continue
544
-
545
- rel_attr = getattr(cls, rel_name)
546
- if not hasattr(rel_attr, "property"):
547
- continue
548
-
549
- # Get the related model class and the back reference attribute
550
- related_model = rel_attr.property.mapper.class_
551
- back_populates = getattr(rel_attr.property, "back_populates", None)
552
-
553
- # Create each related object
554
- for item_data in items_data:
555
- # Set the back reference if it exists
556
- if back_populates:
557
- item_data[back_populates] = obj
558
-
559
- related_obj = related_model(**item_data)
560
- session.add(related_obj)
561
-
562
- await session.commit()
563
-
564
- # Refresh with relationships
565
- await session.refresh(obj, attribute_names=list(related_data.keys()))
566
- return obj
567
566
 
568
567
  @classmethod
569
568
  def _get_unique_fields(cls) -> List[str]:
@@ -591,6 +590,15 @@ class EasyModel(SQLModel):
591
590
  "quantity": 2
592
591
  })
593
592
 
593
+ It also handles lists of related objects for one-to-many relationships:
594
+ publisher = await Publisher.insert({
595
+ "name": "Example Publisher",
596
+ "authors": [
597
+ {"name": "Author 1", "email": "author1@example.com"},
598
+ {"name": "Author 2", "email": "author2@example.com"}
599
+ ]
600
+ })
601
+
594
602
  For each nested object:
595
603
  1. Find the target model class
596
604
  2. Check if an object with the same unique fields already exists
@@ -620,8 +628,8 @@ class EasyModel(SQLModel):
620
628
 
621
629
  # Handle nested relationship objects
622
630
  for key, value in data.items():
623
- # Skip if the value is not a dictionary
624
- if not isinstance(value, dict):
631
+ # Skip if None
632
+ if value is None:
625
633
  continue
626
634
 
627
635
  # Check if this is a relationship field (either by name or derived from foreign key)
@@ -675,105 +683,164 @@ class EasyModel(SQLModel):
675
683
  logging.warning(f"Could not find related model for {key} in {cls.__name__}")
676
684
  continue
677
685
 
678
- # Look for unique fields in the related model to use for searching
679
- unique_fields = []
680
- for field_name, field_info in related_model.__fields__.items():
681
- if (hasattr(field_info, "field_info") and
682
- field_info.field_info.extra.get('unique', False)):
683
- unique_fields.append(field_name)
684
-
685
- # Create a search dictionary using unique fields
686
- search_dict = {}
687
- for field in unique_fields:
688
- if field in value and value[field] is not None:
689
- search_dict[field] = value[field]
690
-
691
- # If no unique fields found but ID is provided, use it
692
- if not search_dict and 'id' in value and value['id']:
693
- search_dict = {'id': value['id']}
694
-
695
- # Special case for products without uniqueness constraints
696
- if not search_dict and related_model.__tablename__ == 'products' and 'name' in value:
697
- search_dict = {'name': value['name']}
698
-
699
- # Try to find an existing record
700
- related_obj = None
701
- if search_dict:
702
- logging.info(f"Searching for existing {related_model.__name__} with {search_dict}")
686
+ # Check if the value is a list (one-to-many) or dict (one-to-one)
687
+ if isinstance(value, list):
688
+ # Handle one-to-many relationship (list of dictionaries)
689
+ related_objects = []
703
690
 
704
- try:
705
- # Create a more appropriate search query based on unique fields
706
- existing_stmt = select(related_model)
707
- for field, field_value in search_dict.items():
708
- existing_stmt = existing_stmt.where(getattr(related_model, field) == field_value)
709
-
710
- existing_result = await session.execute(existing_stmt)
711
- related_obj = existing_result.scalars().first()
712
-
713
- if related_obj:
714
- logging.info(f"Found existing {related_model.__name__} with ID: {related_obj.id}")
715
- except Exception as e:
716
- logging.error(f"Error finding existing record: {e}")
717
-
718
- if related_obj:
719
- # Update the existing record with any non-unique field values
720
- for attr, attr_val in value.items():
721
- # Skip ID field
722
- if attr == 'id':
691
+ for item in value:
692
+ if not isinstance(item, dict):
693
+ logging.warning(f"Skipping non-dict item in list for {key}")
723
694
  continue
724
695
 
725
- # Skip unique fields with different values to avoid constraint violations
726
- if attr in unique_fields and getattr(related_obj, attr) != attr_val:
727
- continue
728
-
729
- # Update non-unique fields
730
- current_val = getattr(related_obj, attr, None)
731
- if current_val != attr_val:
732
- setattr(related_obj, attr, attr_val)
696
+ related_obj = await cls._process_single_relationship_item(
697
+ session, related_model, item
698
+ )
699
+ if related_obj:
700
+ related_objects.append(related_obj)
733
701
 
734
- # Add the updated object to the session
735
- session.add(related_obj)
736
- logging.info(f"Reusing existing {related_model.__name__} with ID: {related_obj.id}")
737
- else:
738
- # Create a new record
739
- logging.info(f"Creating new {related_model.__name__} for {key}")
740
- related_obj = related_model(**value)
741
- session.add(related_obj)
702
+ # For one-to-many, we need to keep a list of related objects to be attached later
703
+ # We'll store them in a special field that will be removed before creating the model
704
+ result[f"_related_{key}"] = related_objects
705
+
706
+ # Remove the original field from the result
707
+ if key in result:
708
+ del result[key]
742
709
 
743
- # Ensure the object has an ID by flushing
744
- try:
745
- await session.flush()
746
- except Exception as e:
747
- logging.error(f"Error flushing session for {related_model.__name__}: {e}")
710
+ elif isinstance(value, dict):
711
+ # Handle one-to-one relationship (single dictionary)
712
+ related_obj = await cls._process_single_relationship_item(
713
+ session, related_model, value
714
+ )
748
715
 
749
- # If there was a uniqueness error, try again to find the existing record
750
- if "UNIQUE constraint failed" in str(e):
751
- logging.info(f"UNIQUE constraint failed, trying to find existing record again")
752
-
753
- # Try to find by any field provided in the search_dict
754
- existing_stmt = select(related_model)
755
- for field, field_value in search_dict.items():
756
- existing_stmt = existing_stmt.where(getattr(related_model, field) == field_value)
757
-
758
- # Execute the search query
759
- existing_result = await session.execute(existing_stmt)
760
- related_obj = existing_result.scalars().first()
716
+ if related_obj:
717
+ # Update the result with the foreign key ID
718
+ foreign_key_name = f"{key}_id"
719
+ result[foreign_key_name] = related_obj.id
761
720
 
762
- if not related_obj:
763
- # We couldn't find an existing record, re-raise the exception
764
- raise
765
-
766
- logging.info(f"Found existing {related_model.__name__} with ID: {related_obj.id} after constraint error")
721
+ # Remove the relationship dictionary from the result
722
+ if key in result:
723
+ del result[key]
724
+
725
+ return result
726
+
727
+ @classmethod
728
+ async def _process_single_relationship_item(cls, session: AsyncSession, related_model: Type, item_data: Dict[str, Any]) -> Optional[Any]:
729
+ """
730
+ Process a single relationship item (dictionary).
731
+
732
+ This helper method is used by _process_relationships_for_insert to handle
733
+ both singular relationship objects and items within lists of relationships.
734
+
735
+ Args:
736
+ session: The database session to use
737
+ related_model: The related model class
738
+ item_data: Dictionary with field values for the related object
739
+
740
+ Returns:
741
+ The created or found related object, or None if processing failed
742
+ """
743
+ # Look for unique fields in the related model to use for searching
744
+ unique_fields = []
745
+ for field_name, field_info in related_model.__fields__.items():
746
+ if (hasattr(field_info, "field_info") and
747
+ field_info.field_info.extra.get('unique', False)):
748
+ unique_fields.append(field_name)
749
+
750
+ # Create a search dictionary using unique fields
751
+ search_dict = {}
752
+ for field in unique_fields:
753
+ if field in item_data and item_data[field] is not None:
754
+ search_dict[field] = item_data[field]
755
+
756
+ # If no unique fields found but ID is provided, use it
757
+ if not search_dict and 'id' in item_data and item_data['id']:
758
+ search_dict = {'id': item_data['id']}
759
+
760
+ # Special case for products without uniqueness constraints
761
+ if not search_dict and related_model.__tablename__ == 'products' and 'name' in item_data:
762
+ search_dict = {'name': item_data['name']}
763
+
764
+ # Try to find an existing record
765
+ related_obj = None
766
+ if search_dict:
767
+ logging.info(f"Searching for existing {related_model.__name__} with {search_dict}")
768
+
769
+ try:
770
+ # Create a more appropriate search query based on unique fields
771
+ existing_stmt = select(related_model)
772
+ for field, field_value in search_dict.items():
773
+ existing_stmt = existing_stmt.where(getattr(related_model, field) == field_value)
767
774
 
768
- # Update the result with the foreign key ID
769
- foreign_key_name = f"{key}_id"
770
- result[foreign_key_name] = related_obj.id
775
+ existing_result = await session.execute(existing_stmt)
776
+ related_obj = existing_result.scalars().first()
771
777
 
772
- # Remove the relationship dictionary from the result
773
- if key in result:
774
- del result[key]
778
+ if related_obj:
779
+ logging.info(f"Found existing {related_model.__name__} with ID: {related_obj.id}")
780
+ except Exception as e:
781
+ logging.error(f"Error finding existing record: {e}")
775
782
 
776
- return result
783
+ if related_obj:
784
+ # Update the existing record with any non-unique field values
785
+ for attr, attr_val in item_data.items():
786
+ # Skip ID field
787
+ if attr == 'id':
788
+ continue
789
+
790
+ # Skip unique fields with different values to avoid constraint violations
791
+ if attr in unique_fields and getattr(related_obj, attr) != attr_val:
792
+ continue
793
+
794
+ # Update non-unique fields
795
+ current_val = getattr(related_obj, attr, None)
796
+ if current_val != attr_val:
797
+ setattr(related_obj, attr, attr_val)
798
+
799
+ # Add the updated object to the session
800
+ session.add(related_obj)
801
+ logging.info(f"Reusing existing {related_model.__name__} with ID: {related_obj.id}")
802
+ else:
803
+ # Create a new record
804
+ logging.info(f"Creating new {related_model.__name__}")
805
+
806
+ # Process nested relationships in this item first
807
+ if hasattr(related_model, '_process_relationships_for_insert'):
808
+ # This is a recursive call to process nested relationships
809
+ processed_item_data = await related_model._process_relationships_for_insert(
810
+ session, item_data
811
+ )
812
+ else:
813
+ processed_item_data = item_data
814
+
815
+ related_obj = related_model(**processed_item_data)
816
+ session.add(related_obj)
817
+
818
+ # Ensure the object has an ID by flushing
819
+ try:
820
+ await session.flush()
821
+ except Exception as e:
822
+ logging.error(f"Error flushing session for {related_model.__name__}: {e}")
823
+
824
+ # If there was a uniqueness error, try again to find the existing record
825
+ if "UNIQUE constraint failed" in str(e):
826
+ logging.info(f"UNIQUE constraint failed, trying to find existing record again")
827
+
828
+ # Try to find by any field provided in the search_dict
829
+ existing_stmt = select(related_model)
830
+ for field, field_value in search_dict.items():
831
+ existing_stmt = existing_stmt.where(getattr(related_model, field) == field_value)
832
+
833
+ # Execute the search query
834
+ existing_result = await session.execute(existing_stmt)
835
+ related_obj = existing_result.scalars().first()
836
+
837
+ if not related_obj:
838
+ # We couldn't find an existing record, re-raise the exception
839
+ raise
840
+
841
+ logging.info(f"Found existing {related_model.__name__} with ID: {related_obj.id} after constraint error")
842
+
843
+ return related_obj
777
844
 
778
845
  @classmethod
779
846
  async def update(cls: Type[T], data: Dict[str, Any], criteria: Dict[str, Any], include_relationships: bool = True) -> Optional[T]:
@@ -997,7 +1064,7 @@ class EasyModel(SQLModel):
997
1064
  Retrieve record(s) by matching attribute values.
998
1065
 
999
1066
  Args:
1000
- criteria: Dictionary of field values to filter by (field=value)
1067
+ criteria: Dictionary of search criteria
1001
1068
  all: If True, return all matching records, otherwise return only the first one
1002
1069
  first: If True, return only the first record (equivalent to all=False)
1003
1070
  include_relationships: If True, eagerly load all relationships
@@ -1110,6 +1177,37 @@ class EasyModel(SQLModel):
1110
1177
  new_record = await cls.insert(data)
1111
1178
  return new_record, True
1112
1179
 
1180
+ @classmethod
1181
+ async def insert_with_related(
1182
+ cls: Type[T],
1183
+ data: Dict[str, Any],
1184
+ related_data: Dict[str, List[Dict[str, Any]]] = None
1185
+ ) -> T:
1186
+ """
1187
+ Create a model instance with related objects in a single transaction.
1188
+
1189
+ Args:
1190
+ data: Dictionary of field values for the main model
1191
+ related_data: Dictionary mapping relationship names to lists of data dictionaries
1192
+ for creating related objects
1193
+
1194
+ Returns:
1195
+ The created model instance with relationships loaded
1196
+ """
1197
+ if related_data is None:
1198
+ related_data = {}
1199
+
1200
+ # Create a copy of data for modification
1201
+ insert_data = data.copy()
1202
+
1203
+ # Add relationship fields to the data
1204
+ for rel_name, items_data in related_data.items():
1205
+ if items_data:
1206
+ insert_data[rel_name] = items_data
1207
+
1208
+ # Use the enhanced insert method to handle all relationships
1209
+ return await cls.insert(insert_data, include_relationships=True)
1210
+
1113
1211
  # Register an event listener to update 'updated_at' on instance modifications.
1114
1212
  @event.listens_for(Session, "before_flush")
1115
1213
  def _update_updated_at(session, flush_context, instances):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: async-easy-model
3
- Version: 0.2.2
3
+ Version: 0.2.3
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
@@ -154,12 +154,36 @@ users = await User.insert([
154
154
  {"username": "user2", "email": "user2@example.com"}
155
155
  ])
156
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
157
+ # Insert with nested relationships
158
+ new_post = await Post.insert({
159
+ "title": "My Post",
160
+ "content": "Content here",
161
+ "user": {"username": "jane_doe"}, # Will automatically link to existing user
162
+ "comments": [ # Create multiple comments in a single transaction
163
+ {"text": "Great post!", "user": {"username": "reader1"}},
164
+ {"text": "Thanks for sharing", "user": {"username": "reader2"}}
165
+ ]
166
+ })
167
+ # Access nested data without requerying
168
+ print(f"Post by {new_post.user.username} with {len(new_post.comments)} comments")
169
+
170
+ # Insert with nested one-to-many relationships
171
+ publisher = await Publisher.insert({
172
+ "name": "Example Publisher",
173
+ "books": [ # List of nested objects
174
+ {
175
+ "title": "Python Mastery",
176
+ "genres": [
177
+ {"name": "Programming"},
178
+ {"name": "Education"}
179
+ ]
180
+ },
181
+ {"title": "Data Science Handbook"}
182
+ ]
162
183
  })
184
+ # Access nested relationships immediately
185
+ print(f"Publisher: {publisher.name} with {len(publisher.books)} books")
186
+ print(f"First book genres: {[g.name for g in publisher.books[0].genres]}")
163
187
  ```
164
188
 
165
189
  ### Read (Retrieve)
@@ -265,22 +289,28 @@ Using the models defined earlier, here's how to work with relationships:
265
289
 
266
290
  ```python
267
291
  # Load all relationships automatically
268
- post = await Post.select({"id": 1}, include_relationships=True)
292
+ post = await Post.select({"id": 1})
269
293
  print(post.user.username) # Access related objects directly
270
294
 
271
295
  # Load specific relationships
272
296
  post = await Post.get_with_related(1, ["user", "comments"])
273
297
 
274
298
  # Load relationships after fetching
275
- post = await Post.select({"id": 1})
299
+ post = await Post.select({"id": 1}, include_relationships=False)
276
300
  await post.load_related(["user", "comments"])
277
301
 
278
- # Insert with related objects in a single transaction
279
- new_post = await Post.insert_with_related({
302
+ # Insert with nested relationships
303
+ new_post = await Post.insert({
280
304
  "title": "My Post",
281
305
  "content": "Content here",
282
- "user": {"username": "john_doe"}
306
+ "user": {"username": "jane_doe"}, # Will automatically link to existing user
307
+ "comments": [ # Create multiple comments in a single transaction
308
+ {"text": "Great post!", "user": {"username": "reader1"}},
309
+ {"text": "Thanks for sharing", "user": {"username": "reader2"}}
310
+ ]
283
311
  })
312
+ # Access nested data without requerying
313
+ print(f"Post by {new_post.user.username} with {len(new_post.comments)} comments")
284
314
 
285
315
  # Convert to dictionary with nested relationships
286
316
  post_dict = post.to_dict(include_relationships=True, max_depth=2)
@@ -325,7 +355,7 @@ db_config.set_connection_url("postgresql+asyncpg://user:password@localhost:5432/
325
355
 
326
356
  ## Documentation
327
357
 
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.
358
+ 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.
329
359
 
330
360
  ## License
331
361
 
@@ -0,0 +1,10 @@
1
+ async_easy_model/__init__.py,sha256=u1yerriPm93E_uXcK9r-y7wcMOV1F4t-KjKEooCaiIA,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=G-0htjy78Nae5HdNE2dMGV8Z8s-rUNRTUANcKArxrDM,57852
5
+ async_easy_model/relationships.py,sha256=vR5BsJpGaDcecCcNlg9-ouZfxFXFQv5kOyiXhKp_T7A,3286
6
+ async_easy_model-0.2.3.dist-info/LICENSE,sha256=uwDkl6oHbRltW7vYKNc4doJyhtwhyrSNFFlPpKATwLE,1072
7
+ async_easy_model-0.2.3.dist-info/METADATA,sha256=SZJy5Ozs7Jn-J3qVTiVB637IGza-5C6hdet_dPocM3I,10720
8
+ async_easy_model-0.2.3.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
9
+ async_easy_model-0.2.3.dist-info/top_level.txt,sha256=e5_47sGmJnyxz2msfwU6C316EqmrSd9RGIYwZyWx68E,17
10
+ async_easy_model-0.2.3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
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,,