Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Field Exclusion

Control which fields appear in which models using the exclude attribute.

Basic Syntax

#[crudcrate(exclude(target1, target2, ...))]
pub field_name: FieldType,

Exclusion Targets

TargetModelHTTP Method
oneResponseGET /items/:id
createCreatePOST /items
updateUpdatePUT /items/:id
listListGET /items

Common Patterns

Auto-Generated IDs

// Don't let clients set the ID
#[crudcrate(primary_key, exclude(create, update))]
pub id: Uuid,

Result:

  • Appears in responses
  • Not in create request model
  • Not in update request model

Timestamps

// Managed by the system
#[crudcrate(exclude(create, update), on_create = chrono::Utc::now())]
pub created_at: DateTimeUtc,

#[crudcrate(exclude(create, update), on_create = chrono::Utc::now(), on_update = chrono::Utc::now())]
pub updated_at: DateTimeUtc,

Sensitive Data

// Never expose password
#[crudcrate(exclude(one, list))]
pub password_hash: String,

// Never expose in responses
#[crudcrate(exclude(one, list))]
pub api_secret: String,

Result:

  • Not in any response
  • Can be set on create
  • Can be updated

Expensive Fields

// Large content not needed in lists
#[crudcrate(exclude(list))]
pub full_content: String,

// Computed field expensive to load
#[crudcrate(exclude(list))]
pub statistics: Json,

Result:

  • In single item response
  • Not in list response
  • Can be set/updated

Internal Fields

// Internal tracking, never expose
#[crudcrate(exclude(one, create, update, list))]
pub internal_score: f64,

Result:

  • Not in any model
  • Field only used internally in database

Read-Only Computed Fields

// Computed by database trigger
#[crudcrate(exclude(create, update))]
pub view_count: i32,

// Calculated field
#[crudcrate(exclude(create, update))]
pub full_name: String,

Generated Models Comparison

Given this entity:

pub struct Model {
    #[crudcrate(primary_key, exclude(create, update))]
    pub id: i32,

    #[crudcrate(filterable)]
    pub email: String,

    #[crudcrate(exclude(one, list))]
    pub password_hash: String,

    pub display_name: Option<String>,

    #[crudcrate(exclude(list))]
    pub bio: Option<String>,

    #[crudcrate(exclude(create, update))]
    pub created_at: DateTimeUtc,
}

Response Model (one)

pub struct User {
    pub id: i32,
    pub email: String,
    // password_hash: excluded
    pub display_name: Option<String>,
    pub bio: Option<String>,
    pub created_at: DateTimeUtc,
}

Create Model (create)

pub struct UserCreate {
    // id: excluded
    pub email: String,
    pub password_hash: String,
    pub display_name: Option<String>,
    pub bio: Option<String>,
    // created_at: excluded
}

Update Model (update)

pub struct UserUpdate {
    // id: excluded
    pub email: Option<String>,
    pub password_hash: Option<String>,
    pub display_name: Option<Option<String>>,
    pub bio: Option<Option<String>>,
    // created_at: excluded
}

List Model (list)

pub struct UserList {
    pub id: i32,
    pub email: String,
    // password_hash: excluded
    pub display_name: Option<String>,
    // bio: excluded
    pub created_at: DateTimeUtc,
}

Visual Reference

Field            │ Response │ Create │ Update │ List
─────────────────┼──────────┼────────┼────────┼──────
id               │   Yes    │   No   │   No   │ Yes
email            │   Yes    │  Yes   │  Yes   │ Yes
password_hash    │   No     │  Yes   │  Yes   │ No
display_name     │   Yes    │  Yes   │  Yes   │ Yes
bio              │   Yes    │  Yes   │  Yes   │ No
created_at       │   Yes    │   No   │   No   │ Yes

Combining with Other Attributes

// Primary key with auto-generation
#[crudcrate(primary_key, exclude(create, update), on_create = Uuid::new_v4())]
pub id: Uuid,

// Filterable but not in create/update
#[crudcrate(filterable, exclude(create, update), on_create = chrono::Utc::now())]
pub created_at: DateTimeUtc,

// Sortable in lists but not in create
#[crudcrate(sortable, exclude(create))]
pub view_count: i32,

Best Practices

1. Always Exclude Auto-Generated Fields

#[crudcrate(primary_key, exclude(create, update))]
pub id: Uuid,

#[crudcrate(exclude(create, update))]
pub created_at: DateTimeUtc,

2. Never Expose Secrets

#[crudcrate(exclude(one, list))]
pub password_hash: String,

#[crudcrate(exclude(one, list))]
pub api_key: String,

#[crudcrate(exclude(one, list))]
pub refresh_token: Option<String>,

3. Optimize List Responses

// Large text fields
#[crudcrate(exclude(list))]
pub content: String,

// Expensive computed fields
#[crudcrate(exclude(list))]
pub statistics: Json,

// Relationships (use join instead)
#[sea_orm(ignore)]
#[crudcrate(non_db_attr, join(one))]  // Not in list
pub comments: Vec<Comment>,

4. Document Your Exclusions

/// User password hash - never exposed in API responses
#[crudcrate(exclude(one, list))]
pub password_hash: String,

/// Created timestamp - managed by system
#[crudcrate(exclude(create, update), on_create = chrono::Utc::now())]
pub created_at: DateTimeUtc,

Troubleshooting

Field Not Appearing

Check that:

  1. Field is not excluded from that model
  2. Field has correct visibility (pub)
  3. Entity compiles without errors

Client Sending Excluded Fields

Excluded fields in requests are silently ignored:

# Trying to set id on create
POST /users
{"id": 999, "email": "[email protected]"}

# Result: id is ignored, auto-generated
{"id": 1, "email": "[email protected]"}

Partial Updates Not Working

For optional fields, remember the double-Option pattern:

pub struct UserUpdate {
    pub bio: Option<Option<String>>,
    // None = don't change
    // Some(None) = set to null
    // Some(Some(value)) = set to value
}

Next Steps