Part 3: Models and Relationships

In this part, you'll create Active Record models for your blog engine and define the relationships between them. You'll learn how to map database tables to Crystal structs and navigate between related records.

What You'll Learn

  • Creating Active Record models

  • Defining model properties

  • Setting up belongs_to relationships

  • Setting up has_many relationships

  • Adding custom model methods

  • Navigating between related records

Prerequisites

Step 1: Create the User Model

# src/models/user.cr
struct User
  include CQL::ActiveRecord::Model(Int64)
  db_context BlogDB, :users

  property id : Int64?
  property username : String
  property email : String
  property first_name : String?
  property last_name : String?
  property active : Bool = true
  property created_at : Time?
  property updated_at : Time?

  # Relationships
  has_many :posts, Post, :user_id
  has_many :comments, Comment, :user_id

  def initialize(
    @username : String,
    @email : String,
    @first_name : String? = nil,
    @last_name : String? = nil,
    @active : Bool = true
  )
  end

  # Custom methods
  def full_name : String
    if first_name && last_name
      "#{first_name} #{last_name}"
    elsif first_name
      first_name.not_nil!
    else
      username
    end
  end

  def display_name : String
    first_name || username
  end
end

Key points:

  • include CQL::ActiveRecord::Model(Int64) - Makes this an Active Record model with Int64 primary key

  • db_context BlogDB, :users - Connects model to the users table in BlogDB

  • property defines model attributes matching database columns

  • has_many defines one-to-many relationships

  • Custom methods add business logic

Step 2: Create the Category Model

The generate_slug method automatically creates URL-friendly slugs from category names.

Step 3: Create the Post Model

Key points:

  • belongs_to defines many-to-one relationships

  • category_id is nilable (Int64?) because category is optional

  • Custom methods provide useful functionality like word count and excerpts

Step 4: Create the Comment Model

Step 5: Update the Main File

Update blog_engine.cr to require all models:

Note: Order matters! Models with dependencies must be required after their dependencies.

Step 6: Test the Models

Create a test script:

Run it:

Understanding Relationships

belongs_to

This creates a user method that returns the associated User (or nil):

has_many

This creates a posts method that returns a query builder:

Model Best Practices

  1. Keep models focused: Business logic belongs in models, not controllers

  2. Use meaningful method names: published? is clearer than is_published

  3. Validate early: Add validations to catch errors before database operations

  4. Handle nil safely: Use try(&.method) when accessing optional relationships

  5. Add useful computed properties: Like full_name, word_count, etc.

Summary

In this part, you:

  1. Created four Active Record models: User, Category, Post, Comment

  2. Defined properties matching database columns

  3. Set up belongs_to and has_many relationships

  4. Added custom methods for business logic

  5. Tested model creation and relationship navigation

Next Steps

In Part 4: CRUD Operations, you'll learn how to create, read, update, and delete records efficiently, including bulk operations and querying patterns.


Tutorial Navigation:

Last updated

Was this helpful?