Active Record Models

Models

With our routes, controllers, and views all converted to use Rails' conventions and conveniences, we have one last big piece to change: our models.

What we're going to do next feels like a bit of magic - we're just going to let our models Post and Comment inherit from ActiveRecord::Base and remove all the code:

### app/models/comment.rb ###

class Comment < ActiveRecord::Base
end
### app/models/post.rb ###

class Post < ActiveRecord::Base
end

That's right, we can get rid of the entirety of the content of our classes now that we're inheriting from ActiveRecord::Base. Remember how we were sharing some functionality between our models using BaseModel earlier? Well, ActiveRecord::Base provides all of the functionality we've been using thus far, using (mostly) the same method names.

We went through this process mostly to show you what ActiveRecord::Base actually does under the hood - you can always check your original models to understand what Rails provides to you as a framework that you don't have to implement yourself.

When we use Active Record based models, and we follow the convention like below:

Model Name File Location and Name Database Table Name
Post app/models/post.rb posts
MountainBike app/models/mountain_bike.rb mountain_bikes

Rails will automatically map a model to a database table if you follow this naming convention. Inheriting from ActiveRecord::Base will allow you to access data records in the database directly through your models:

### find a post, read its attributes ###

post = Post.find 1
#  Post Load (0.1ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1  [["id", 1]]
#=> #<Post:0x00000007360e78
# id: 1,
# title: "Blog 1",
# body: "Lorem ipsum dolor sit amet.",
# author: "Brad",
# created_at: Sun, 07 Dec 2014 00:00:00 UTC +00:00>

post.title
# => "Blog 1"
post.body
# => "Lorem ipsum dolor sit amet."
post.created_at
# => Sun, 07 Dec 2014 00:00:00 UTC +00:00
post.title="My Blog"
# => "My Blog"
post.save
# => true

Validations

We have our Active Record based models set up, but they don't have validations ready yet:

### check validity of empty post ###

post = Post.new
# => #<Post:0x0000000252fbc8
#  id: nil,
#  title: nil,
#  body: nil,
#  author: nil,
#  created_at: nil>
post.valid?
# => true
post.errors
# => #<ActiveModel::Errors:0x00000003664bc8
#  @base=
#   #<Post:0x0000000252fbc8
#    id: nil,
#    title: nil,
#    body: nil,
#    author: nil,
#    created_at: nil>,
#  @messages={}>

Rails allows you to define validations in a very easy way:

### app/models/post.rb ###

class Post < ActiveRecord::Base
  validates_presence_of :title, :body, :author
end
### app/models/comment.rb ###

class Comment < ActiveRecord::Base
  validates_presence_of :body, :author
end

Let's take a look how this works:

### Post validation ###

post = Post.new
# => #<Post:0x000000074be7e8
#  id: nil,
#  title: nil,
#  body: nil,
#  author: nil,
#  created_at: nil>

post.valid?
# => false

post.errors.messages
# => {:title=>["can't be blank"],
#  :body=>["can't be blank"],
#  :author=>["can't be blank"]}


### Comment validation ###

comment = Comment.new
# => #<Comment:0x00000007676dd8
#  id: nil,
#  body: nil,
#  author: nil,
#  post_id: nil,
#  created_at: nil>

comment.valid?
# => false

comment.errors.messages
# => {:body=>["can't be blank"], :author=>["can't be blank"]}

Validation impacts on a record's persistence:

post = Post.new
# => #<Post:0x00000005aaece0
#  id: nil,
#  title: nil,
#  body: nil,
#  author: nil,
#  created_at: nil,
#  updated_at: nil>

post.valid?
# => false

# as expected, this invalid instance returns false here
post.save
#    (0.3ms)  begin transaction
#    (0.0ms)  rollback transaction
# => false

# but if we call the "bang" (!) version of the method...
post.save!
#    (0.2ms)  begin transaction
#    (0.0ms)  rollback transaction
# ActiveRecord::RecordInvalid: Validation failed: Title can't be blank, Body can't be blank, Author can't be blank

# it throws an error. this convention repeats throughout Rails.

# with `.create` for example...
Post.create({title:'foo', body:'bar', author:'baz'})
#    (0.1ms)  begin transaction
#   SQL (0.2ms)  INSERT INTO "posts" ("title", "body", "author", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["title", "foo"], ["body", "bar"], ["author", "baz"], ["created_at", "2015-01-31 10:59:33.931414"], ["updated_at", "2015-01-31 10:59:33.931414"]]
#    (13.1ms)  commit transaction
# => #<Post:0x0000000537a870 id: 4, title: "foo", body: "bar", author: "baz", ...>

Post.create({})
#   (0.1ms)  begin transaction
#   (0.1ms)  rollback transaction
#=> #<Post:0x0000000553c820
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil,
# updated_at: nil>

# with create, we always get back a `Post` object

# but with invalid attributes sent to `.create!`
Post.create!({})
#    (0.1ms)  begin transaction
#    (0.1ms)  rollback transaction
# ActiveRecord::RecordInvalid: Validation failed: Title can't be blank, Body can't be blank, Author can't be blank

# we get an error instead

Other than validate_presence_of, there are other validations that you can use with Rails. Here are some other validations that you may often use:

  • validates_uniqueness_of
  • validates_numericality_of
  • validates_length_of

There other ways of defining validations as well. We may use the validates method, and with that method we can specify which attributes to validate and what to validate. For instance,

validates :name, presence: true

validates the name attribute by checking that it is present before saving. When using validations in this form, we pass the types of validation as k-v pairs of a hash.

validates :name, presence: true, uniqueness: true

By adding uniqueness: true, we may also check that a name is unique along with checking that it is present in our model.
There's even more to learn about validations, but we won't be covering it here. For more information on validations, check out the Rails documentation and the Rails guides page.

Callbacks

Callbacks are methods that "hook" into the lifecycle of an Active Record object. They may be used to run logic whenever an object is created, saved, updated, deleted, validated, or loaded from the database. Here is a list of available callbacks that may be used in Rails:

  • after_initialize
  • after_find
  • after_touch
  • before_validation, after_validation
  • before_save, around_save, after_save
  • before_create, around_create, after_create
  • before_update, around_update, after_update,
  • before_destroy, around_destroy, after_destroy
  • after_commit
  • after_rollback

For more information on Active Record callbacks, you may refer to the Ruby on Rails API page or the Rails guide on Active Record Callbacks.