CRUD Operations

CQL Active Record models provide a comprehensive set of methods for performing CRUD (Create, Read, Update, Delete) operations on your database records. These methods are designed to be intuitive and align with common Active Record patterns.

This guide assumes you have a model defined, for example:

struct User
  include CQL::ActiveRecord::Model(Int64)
  db_context AcmeDB, :users
  property id : Int64?
  property name : String
  property email : String
  property active : Bool = false
  # ... other properties and initializers ...
end

1. Creating Records

There are several ways to create new records and persist them to the database.

new + save / save!

The most fundamental way is to create a new instance of your model using new and then call save or save! to persist it.

  • save: Attempts to save the record. Runs validations. If successful, populates the id (if auto-generated) and returns true. If validations fail or another before_save callback halts the chain, it returns false and does not persist the record. The errors collection on the instance can be inspected.

  • save!: Similar to save, but raises an exception if the record cannot be saved.

    • Raises CQL::Errors::RecordInvalid if validations fail.

    • May raise other database-related exceptions or CQL::Errors::RecordNotSaved for other save failures.

create / create! (Class Methods)

These class methods provide a convenient way to instantiate and save a record in a single step.

  • Model.create(attributes): Creates a new instance with the given attributes (as a Hash or keyword arguments) and attempts to save it. Returns the model instance (which might be invalid and not persisted if save failed) or false if a before_create callback halted the process.

  • Model.create!(attributes): Similar to create, but calls save! internally. It will raise an exception (CQL::Errors::RecordInvalid or other) if the record cannot be created and persisted.

find_or_create_by (Class Method)

This method attempts to find a record matching the given attributes. If found, it returns that record. If not found, it creates and persists a new record with those attributes (plus any additional ones provided if the find attributes are a subset of create attributes).

  • It uses create! internally for the creation part, so it can raise exceptions if the creation fails (e.g., due to validations).


2. Reading Records

CQL provides various methods to retrieve records from the database. Many of these are covered in detail in the Querying Guide.

Summary of Common Finders:

  • Model.all: Retrieves all records of the model type. Returns Array(Model).

  • Model.find?(id : Pk) (or Model.find(id : Pk)): Finds a record by its primary key. Returns Model? (the instance or nil).

  • Model.find!(id : Pk): Finds a record by its primary key. Returns Model or raises DB::NoResultsError (or similar if not found).

  • Model.find_by(**attributes): Finds the first record matching the given attributes. Returns Model?.

  • Model.find_by!(**attributes): Finds the first record matching attributes. Returns Modelor raisesDB::NoResultsError.

  • Model.find_all_by(**attributes): Finds all records matching attributes. Returns Array(Model).

  • Model.first: Retrieves the first record (ordered by primary key). Returns Model?.

  • Model.last: Retrieves the last record (ordered by primary key). Returns Model?.

  • Model.count: Returns the total number of records as Int64.

  • Model.query.[condition].count: Counts records matching specific conditions.

For building more complex queries (e.g., with joins, specific selections, grouping), refer to the Querying Guide.


3. Updating Records

To update existing records, you typically load an instance, modify its attributes, and then save it.

Load, Modify, and save / save!

This is the standard approach:

update / update! (Instance Methods)

These instance methods provide a shortcut to assign attributes and then save the record.

  • instance.update(attributes): Assigns the given attributes (Hash or keyword arguments) to the instance and then calls save. Returns true if successful, false otherwise.

  • instance.update!(attributes): Assigns attributes and calls save!. Raises an exception if saving fails (e.g., CQL::Errors::RecordInvalid).

Model.update!(id, attributes) (Class Method)

This class method updates a record identified by its primary key with the given attributes. It typically loads the record, updates attributes, and calls save!. Can raise DB::NoResultsError if the ID is not found, or validation/save exceptions.

Batch Updates (Model.update_by, Model.update_all)

These methods allow updating multiple records at once without instantiating each one.

  • Model.update_by(conditions_hash, updates_hash): Updates all records matching conditions_hash with the attributes in updates_hash.

  • Model.update_all(updates_hash): Updates all records in the table with the attributes in updates_hash. Use with extreme caution!

These methods typically execute a single SQL UPDATE statement and do not instantiate records, run validations, or trigger callbacks.


4. Deleting Records

Records can be deleted individually or in batches.

delete! (Instance Method)

Deletes the specific model instance from the database.

  • Runs before_destroy and after_destroy callbacks.

  • Returns true if successful. Returns false if a before_destroy callback halts the operation.

Model.delete!(id : Pk) (Class Method)

Deletes the record with the specified primary key directly from the database.

  • This method typically does not run Active Record callbacks (before_destroy, after_destroy) as it usually issues a direct SQL DELETE command.

  • It might raise an exception if the record doesn't exist or if there's a database error.

Batch Deletes (Model.delete_by!, Model.delete_all)

These class methods delete multiple records based on conditions or all records from a table.

  • Model.delete_by!(attributes_hash): Deletes all records matching the attributes_hash.

  • Model.delete_all: Deletes all records from the model's table. Use with extreme caution!

These methods typically execute direct SQL DELETE statements and do not instantiate records or run Active Record callbacks.

Always be careful with batch delete operations, especially delete_all, as they can lead to irreversible data loss if not used correctly.


This guide covers the primary CRUD operations available in CQL Active Record. For more advanced querying, refer to the Querying Guide, and for information on lifecycle events and data integrity, see the guides on Callbacks and Validations.

Last updated

Was this helpful?