# ActiveModel Guide
Ormkit provides two ActiveModel patterns for tracking field changes. This guide explains when to use each.
## Overview
ActiveModel enables efficient partial updates by tracking which fields have changed:
```rust
let mut model = user.into_active_model();
model.name = ActiveValue::Set("New Name");
// Only 'name' field will be updated in database
repo.update(model).await?;
```
## Two Patterns
### 1. Typed ActiveModel (Recommended)
**Compile-time safe field tracking with full type checking.**
```rust
#[derive(ormkit::Entity)]
#[ormkit(table = "users")]
struct User {
#[ormkit(id)]
id: Uuid,
email: String,
name: String,
}
// Generated by derive macro
struct UserActiveModel {
pub id: ActiveValue<Uuid>,
pub email: ActiveValue<String>,
pub name: ActiveValue<String>,
}
```
**Benefits:**
- ✅ Compile-time type checking
- ✅ IDE autocomplete for all fields
- ✅ No runtime string errors
- ✅ Diesel-grade guarantees
**When to use:**
- Most applications (default choice)
- When you want maximum type safety
- When IDE support is important
### 2. HashMap ActiveModel (Escape Hatch)
**Runtime field tracking with dynamic string keys.**
```rust
use ormkit::active_model::ActiveModel;
let mut model = ActiveModel::<User>::default();
model.set("email", "user@example.com");
model.set("name", "John Doe");
```
**Benefits:**
- ✅ Dynamic field access by string
- ✅ Useful for admin panels
- ✅ Generic code that works with any entity
**When to use:**
- Admin panels with dynamic forms
- Generic CRUD builders
- When field names are only known at runtime
---
## Typed ActiveModel Usage
### Basic Updates
```rust
use ormkit::{Entity, Repository};
use ormkit::active_value::ActiveValue;
let user = repo.find_by_id(user_id).await?;
// Convert to ActiveModel
let mut model = user.into_active_model();
// Update specific fields
model.name = ActiveValue::Set("Jane Doe".to_string());
model.email = ActiveValue::Set("jane@example.com".to_string());
// Save (only changed fields are updated)
let updated = repo.update(model).await?;
```
### Field States
```rust
use ormkit::active_value::ActiveValue;
// Set a new value
model.name = ActiveValue::Set("New Name");
// Keep existing value (no update)
model.email = ActiveValue::NotSet;
// Reset to default/None
model.bio = ActiveValue::NotSet; // Won't be included in UPDATE
```
### Partial Updates
```rust
// Only update fields that are Set
let mut model = user.into_active_model();
model.name = ActiveValue::Set("Updated");
model.email = ActiveValue::NotSet; // Won't be updated
repo.update(model).await?;
// Generated SQL: UPDATE users SET name = $1 WHERE id = $2
```
---
## HashMap ActiveModel Usage
### Dynamic Updates
```rust
use ormkit::active_model::ActiveModel;
let mut model = ActiveModel::<User>::default();
// Set fields dynamically
model.set("name", "John Doe");
model.set("email", "john@example.com");
// Check if field is modified
if model.is_modified("name") {
println!("Name will be updated");
}
// Get changes
for (field, value) in model.get_changes() {
println!("{}: {:?}", field, value);
}
```
### Generic CRUD
```rust
async fn update_entity<T: Entity>(
repo: &Repository<T>,
id: T::Id,
updates: HashMap<String, String>,
) -> Result<T, OrmkitError> {
let entity = repo.find_by_id(id).await?;
let mut model = ActiveModel::from_entity(entity);
for (field, value) in updates {
model.set(&field, value);
}
repo.update(model).await
}
```
---
## Migration Path
### From HashMap to Typed
**Current (HashMap):**
```rust
let mut model = ActiveModel::<User>::default();
model.set("email", "user@example.com");
```
**Migrate to (Typed):**
```rust
let mut model = UserActiveModel::default();
model.email = ActiveValue::Set("user@example.com".to_string());
```
### Benefits of Migration
1. **Type Safety**: Catch errors at compile time
2. **IDE Support**: Autocomplete for field names
3. **Performance**: No string hashing overhead
4. **Refactoring**: Field renames caught by compiler
---
## Recommendation
**Use Typed ActiveModel by default.**
Only use HashMap ActiveModel when you truly need dynamic field access (admin panels, generic builders).
The typed approach provides:
- Better error messages
- IDE autocomplete
- Compile-time guarantees
- Easier refactoring
HashMap is an escape hatch for specific use cases, not the default path.
---
## See Also
- [Core ORM Guide](CORE_GUIDE.md) - Basic ORM usage
- [Type-Safe Queries](CORE_GUIDE.md#type-safe-queries) - Query DSL
- [Production Guide](PRODUCTION_GUIDE.md) - Production considerations