Rails desde cero (II): Introducción a ActiveRecord
Segunda entrega de la serie Rails desde cero. Tiempo de lectura estimado: 10 minutos.
ActiveRecord es el corazón de cualquier aplicación Rails. Es el ORM (Object-Relational Mapper) que traduce las filas de tu base de datos en objetos Ruby, y tus objetos Ruby en SQL. Gracias a él puedes trabajar con los datos de forma expresiva y natural sin escribir casi una sola línea de SQL.
En este artículo veremos los fundamentos: cómo se definen los modelos, cómo funcionan las asociaciones, cómo se validan los datos y cómo se hacen consultas. En el siguiente artículo profundizaremos en los aspectos avanzados que marcan la diferencia en proyectos reales.
El modelo y la tabla

La convención básica de ActiveRecord es que cada modelo se corresponde con una tabla en la base de datos. El nombre del modelo es singular y en CamelCase; el de la tabla es plural y en snake_case. Rails hace la conversión automáticamente:
| Modelo | Tabla |
|---|---|
Article |
articles |
User |
users |
BlogPost |
blog_posts |
OrderItem |
order_items |
Todo modelo de Rails hereda de ApplicationRecord:
class Article < ApplicationRecord
end
Con solo eso, Rails ya sabe cómo mapear el modelo a la tabla articles, qué columnas tiene (las lee del esquema) y cómo hacer operaciones CRUD sobre él. No hace falta declarar atributos ni tipos: ActiveRecord los infiere directamente de la base de datos.
CRUD: las operaciones básicas
ActiveRecord te da métodos para las cuatro operaciones fundamentales sobre cualquier registro.
Crear
# new + save (dos pasos)
article = Article.new(title: "Mi primer artículo", body: "Hola mundo")
article.save # => true si pasa las validaciones, false si no
# create (un solo paso, devuelve el objeto)
article = Article.create(title: "Mi primer artículo", body: "Hola mundo")
# create! (lanza excepción si falla)
article = Article.create!(title: "Mi primer artículo", body: "Hola mundo")
La diferencia entre save y save! (o entre create y create!) es importante: los métodos con ! lanzan una excepción ActiveRecord::RecordInvalid si el registro no pasa las validaciones, mientras que los que no la llevan devuelven false. En aplicaciones reales, create! y save! son más seguros porque te obligan a manejar el error explícitamente.
Leer
# Por id (lanza ActiveRecord::RecordNotFound si no existe)
Article.find(1)
# Por condiciones (devuelve nil si no existe)
Article.find_by(title: "Mi primer artículo")
# Todos los registros
Article.all
# Con condiciones
Article.where(published: true)
# El primero y el último
Article.first
Article.last
Actualizar
article = Article.find(1)
# update_attributes
article.update(title: "Título actualizado")
# Asignación directa + save
article.title = "Título actualizado"
article.save
Eliminar
article = Article.find(1)
article.destroy # ejecuta callbacks y validaciones
article.delete # elimina directamente sin callbacks
La distinción entre destroy y delete es relevante: destroy ejecuta todos los callbacks definidos en el modelo (lo veremos más adelante), mientras que delete va directo a la base de datos. En general, usa destroy salvo que tengas una razón de rendimiento para lo contrario.
Validaciones

Las validaciones son reglas que un registro debe cumplir para poder guardarse en la base de datos. Se definen en el modelo y se ejecutan automáticamente antes de cualquier save o create.
class Article < ApplicationRecord
validates :title, presence: true, length: { minimum: 5, maximum: 100 }
validates :body, presence: true
validates :slug, uniqueness: true, format: { with: /\A[a-z0-9-]+\z/ }
validates :status, inclusion: { in: %w[draft published archived] }
end
Los validadores más habituales son:
presence: el campo no puede estar vacíouniqueness: el valor debe ser único en la tablalength: controla la longitud de stringsnumericality: verifica que el valor sea numéricoformat: valida contra una expresión regularinclusion/exclusion: el valor debe (o no debe) estar en una lista
Cuando un registro no pasa las validaciones, Rails lo indica en el objeto errors:
article = Article.new(title: "Hi")
article.valid? # => false
article.errors.full_messages
# => ["Title is too short (minimum is 5 characters)", "Body can't be blank"]
article.errors[:title]
# => ["is too short (minimum is 5 characters)"]
Puedes también escribir validaciones personalizadas:
class Article < ApplicationRecord
validate :title_cannot_contain_forbidden_words
private
def title_cannot_contain_forbidden_words
forbidden = %w[spam clickbait]
if forbidden.any? { |word| title.to_s.downcase.include?(word) }
errors.add(:title, "contiene palabras no permitidas")
end
end
end
Asociaciones
Las asociaciones definen las relaciones entre modelos. ActiveRecord soporta los tipos más comunes con una sintaxis muy declarativa.
belongs_to y has_many
La asociación más frecuente: un artículo pertenece a un autor, y un autor tiene muchos artículos.
class User < ApplicationRecord
has_many :articles, dependent: :destroy
end
class Article < ApplicationRecord
belongs_to :user
end
La tabla articles tendrá una columna user_id que es la clave foránea. Rails lo infiere del nombre de la asociación.
Con esto disponemos de métodos muy expresivos:
user = User.find(1)
user.articles # todos los artículos del usuario
user.articles.count # cuántos tiene
user.articles.create!(title: "Nuevo", body: "Contenido") # crear asociado
article = Article.find(1)
article.user # el usuario al que pertenece
article.user.name # acceder a atributos del usuario
has_one
Cuando la relación es de uno a uno:
class User < ApplicationRecord
has_one :profile
end
class Profile < ApplicationRecord
belongs_to :user
end
has_many :through
Para relaciones muchos a muchos con una tabla intermedia:
class Article < ApplicationRecord
has_many :article_tags
has_many :tags, through: :article_tags
end
class Tag < ApplicationRecord
has_many :article_tags
has_many :articles, through: :article_tags
end
class ArticleTag < ApplicationRecord
belongs_to :article
belongs_to :tag
end
article.tags # todos los tags del artículo
tag.articles # todos los artículos con ese tag
article.tags << Tag.find_by(name: "rails") # añadir un tag
has_and_belongs_to_many
Una alternativa más sencilla para muchos a muchos sin necesidad de modelo intermedio, cuando la tabla de unión no necesita atributos propios:
class Article < ApplicationRecord
has_and_belongs_to_many :tags
end
En la práctica, has_many :through es preferible porque te da más control y flexibilidad si la relación evoluciona.
Consultas básicas

ActiveRecord tiene una API de consultas muy expresiva que genera SQL de forma automática.
# Condiciones simples
Article.where(published: true)
Article.where(status: ["draft", "published"])
# Condiciones con string (cuidado con la inyección SQL)
Article.where("created_at > ?", 1.week.ago)
Article.where("title LIKE ?", "%rails%")
# Ordenar
Article.order(created_at: :desc)
Article.order(:title)
# Limitar resultados
Article.limit(10)
Article.offset(20).limit(10) # paginación manual
# Seleccionar columnas específicas
Article.select(:id, :title, :published_at)
# Contar, sumar, promediar
Article.count
Article.where(published: true).count
Article.average(:reading_time)
Una de las grandes ventajas de ActiveRecord es que las consultas son lazy: no se ejecutan hasta que realmente necesitas los datos. Esto permite encadenar condiciones de forma natural:
query = Article.where(published: true)
query = query.where("created_at > ?", 1.month.ago) if filter_recent
query = query.order(:title) if sort_by_title
query.limit(10) # aquí se ejecuta el SQL
Scopes: consultas reutilizables
Los scopes te permiten dar nombre a consultas frecuentes y reutilizarlas de forma encadenable:
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(user) { where(user: user) }
end
Article.published # artículos publicados
Article.published.recent # publicados, más recientes primero
Article.published.recent.limit(5) # los 5 más recientes publicados
Article.by_author(current_user).recent # del usuario actual, ordenados
Los scopes son equivalentes a métodos de clase, pero tienen la ventaja de ser siempre encadenables y de devolver siempre un ActiveRecord::Relation (nunca nil), lo que los hace más seguros en cadenas de consultas.
Callbacks
Los callbacks son métodos que se ejecutan automáticamente en momentos concretos del ciclo de vida de un registro: antes de guardarse, después de crearse, antes de eliminarse, etc.
class Article < ApplicationRecord
before_save :generate_slug
after_create :notify_subscribers
before_destroy :archive_comments
private
def generate_slug
self.slug = title.parameterize
end
def notify_subscribers
NotificationJob.perform_later(id)
end
end
Los hooks disponibles son:
before_validation/after_validationbefore_save/after_savebefore_create/after_createbefore_update/after_updatebefore_destroy/after_destroyafter_commit/after_rollback
Los callbacks son útiles, pero hay que usarlos con cabeza. Un modelo lleno de callbacks que disparan jobs, envían emails y modifican otros modelos se vuelve muy difícil de testear y razonar. Eso lo veremos en detalle en el artículo avanzado.
Migraciones y el esquema
Ya vimos las migraciones en el artículo de fundamentos, pero vale la pena recordar la relación directa con los modelos: el esquema manda. ActiveRecord lee las columnas de la base de datos en tiempo de ejecución, así que si añades una columna en una migración, el modelo la tendrá disponible automáticamente sin tocar el código Ruby.
# Añadir una columna a articles
bin/rails generate migration AddPublishedAtToArticles published_at:datetime
# La migración generada:
class AddPublishedAtToArticles < ActiveRecord::Migration[7.1]
def change
add_column :articles, :published_at, :datetime
end
end
bin/rails db:migrate
A partir de ahí, article.published_at ya funciona sin más cambios.
Resumen
Con lo que hemos visto en este artículo ya tienes las herramientas para hacer prácticamente cualquier cosa básica con ActiveRecord:
- Crear, leer, actualizar y eliminar registros con una API expresiva en Ruby.
- Definir validaciones que protegen la integridad de tus datos.
- Modelar relaciones entre entidades con asociaciones.
- Escribir consultas legibles y encadenables.
- Reutilizar lógica de consulta con scopes.
- Reaccionar a eventos del ciclo de vida con callbacks.
En el siguiente artículo subimos el nivel: consultas N+1 y cómo evitarlas, eager loading, consultas complejas, el uso correcto de callbacks, y patrones para mantener los modelos limpios cuando la lógica de negocio crece.
¿Algo que no haya quedado claro? Déjalo en los comentarios.