Working with Models¶
Once you've defined a model, you can create, read, update, and delete records. All models inherit from Model, which provides the objects manager for querying, instance methods for persistence, and built-in version history.
All operations have both sync and async variants.
Creating Objects¶
Instantiate a model and call save() to persist it:
person = Person(first_name='John', last_name='Doe')
person.country = 'United States'
person.save()
person = Person(first_name='John', last_name='Doe')
person.country = 'United States'
await person.asave()
Field validation (types, required fields, validators) runs at instantiation. The record is written to the database only when you call save() / asave().
Nested references are saved automatically — if a referenced object hasn't been saved yet, save() persists it first:
event = Event(name='Birthday', person=Person(first_name='John', last_name='Doe'))
event.save() # saves Person first, then Event
Warning
Unsaved objects cannot be used in queries. Person.objects.filter(profile=unsaved_profile) won't match anything because the object doesn't exist in the database yet.
Bulk Create¶
For creating multiple records efficiently:
people = [
Person(first_name='Alice', last_name='Smith'),
Person(first_name='Bob', last_name='Jones'),
]
Person.objects.bulk_create(people)
await Person.objects.bulk_acreate(people)
Updating Objects¶
Fetch an object, modify it, and call save():
person = Person.objects.get(first_name='John', last_name='Doe').execute()
person.country = 'United Kingdom'
person.save()
person = await Person.objects.get(first_name='John', last_name='Doe').aexecute()
person.country = 'United Kingdom'
await person.asave()
Each save() creates a new version of the object. With a historical connection, all previous versions are preserved. With a state connection, the record is overwritten but history is still tracked in the Lakehouse.
Bulk Update¶
for person in people:
person.country = 'Canada'
Person.objects.bulk_update(people)
await Person.objects.bulk_aupdate(people)
Refetch from Database¶
Reload the object's current state from the database:
person = person.refetch_from_db()
# Or fetch the latest version specifically:
person = person.refetch_from_db(latest=True)
person = await person.arefetch_from_db()
Deleting Objects¶
person = Person.objects.get(first_name='John', last_name='Doe').execute()
person.delete()
person = await Person.objects.get(first_name='John', last_name='Doe').aexecute()
await person.adelete()
With a state connection, the record is removed. With a historical connection, a new version is created with a deleted flag.
Bulk Delete¶
Person.objects.bulk_delete(people)
await Person.objects.bulk_adelete(people)
Version History¶
AMSDAL tracks every change as a version. You can navigate between versions of any object:
person = Person.objects.get(first_name='John').execute()
# Get the previous version
prev = person.previous_version()
# Get the next version (if navigating from an older version)
nxt = person.next_version()
person = await Person.objects.get(first_name='John').aexecute()
prev = await person.aprevious_version()
nxt = await person.anext_version()
Both return None if there is no previous/next version.
Fetching a Specific Version¶
specific = Person.objects.get_specific_version(
object_id='abc123',
object_version='v2',
)
specific = await Person.objects.aget_specific_version(
object_id='abc123',
object_version='v2',
)
References¶
When a model has a reference (foreign key) to another model, AMSDAL loads the referenced object automatically on access:
class Event(Model):
name: str
person: Person
event = Event.objects.get(name='Birthday').execute()
print(event.person.first_name)
#> John
event = await Event.objects.get(name='Birthday').aexecute()
print(event.person.first_name)
#> John
Frozen References¶
By default, a reference points to the latest version of the referenced object. To pin a reference to a specific version, pass a frozen Reference object instead of the model instance:
person = Person.objects.get(first_name='John', age=18).execute()
# Create a frozen reference to the current version
frozen_ref = person.build_reference(is_frozen=True)
event = Event(name='Birthday 18', person=frozen_ref, date='2023-02-20')
event.save()
person = await Person.objects.get(first_name='John', age=18).aexecute()
frozen_ref = await person.abuild_reference(is_frozen=True)
event = Event(name='Birthday 18', person=frozen_ref, date='2023-02-20')
await event.asave()
If person.age is updated later, this event still references the version where age=18.
ReferenceLoader¶
You can load objects from a Reference object using ReferenceLoader:
from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
ref = person.build_reference()
loaded_person = ReferenceLoader(ref).load_reference()
from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
ref = await person.abuild_reference()
loaded_person = await ReferenceLoader(ref).aload_reference()
Serialization¶
Models provide Pydantic's model_dump() and additional AMSDAL-specific methods for serializing with references:
# Standard Pydantic serialization
data = person.model_dump()
json_str = person.model_dump_json()
# With references (foreign keys serialized as reference objects)
data = person.model_dump_refs()
json_str = person.model_dump_json_refs()
ExternalModel¶
ExternalModel provides read-only access to existing database tables that AMSDAL doesn't manage. No migrations, no versioning — just querying:
from amsdal_models.classes.external_model import ExternalModel
class LegacyUser(ExternalModel):
__table_name__ = 'users'
__connection__ = 'legacy_db'
id: int
username: str
email: str
Query it like a regular model:
users = LegacyUser.objects.filter(username='alice').execute()
users = await LegacyUser.objects.filter(username='alice').aexecute()
ExternalModel does not have save(), delete(), lifecycle hooks, or metadata. It's designed for integrating with external databases you don't control.
| Feature | Model | ExternalModel |
|---|---|---|
| CRUD operations | Full | Read-only |
| Version history | Yes | No |
| Migrations | Yes | No |
| Lifecycle hooks | Yes | No |
| References | Yes | No |
objects manager |
Full Manager | ExternalManager |