-
Notifications
You must be signed in to change notification settings - Fork 0
17b Categories Posts Migration
All we need is a foreign key column—category_id
—on the posts
table. You could simply add that column as an integer and be done with it.
We'll explore a cooler, and equally easy, approach.
When creating a table, you can use a column type of references
to define a foreign key column.
create_table :posts do |t|
t.references :category
end
You can also add an index simultaneously (always a good idea for foreign key columns).
create_table :posts do |t|
t.references :category, index: true
end
Now to put this knowledge to good use. Generate a migration as always.
$ bin/rails g migration add_category_id_to_posts
invoke active_record
create db/migrate/20141105034340_add_category_id_to_posts.rb
And edit your migration as follows.
class AddCategoryIdToPosts < ActiveRecord::Migration
def change
add_reference :posts, :category, index: true
end
end
Now migrate!
$ bin/rake db:migrate
== 20141105034340 AddCategoryIdToPosts: migrating =============================
-- add_reference(:posts, :category, {:index=>true})
-> 0.0073s
== 20141105034340 AddCategoryIdToPosts: migrated (0.0074s) ====================
Let's check out the updated posts
table in the database. Remember how to do that?
$ psql -d bluit_development
psql (9.3.2)
Type "help" for help.
bluit_development=# \d posts
Table "public.posts"
Column | Type | Modifiers
-------------+-----------------------------+----------------------------------------------------
id | integer | not null default nextval('posts_id_seq'::regclass)
title | character varying(255) |
link | character varying(2000) |
body | text |
created_at | timestamp without time zone |
updated_at | timestamp without time zone |
post_type | integer | default 0
category_id | integer |
Indexes:
"posts_pkey" PRIMARY KEY, btree (id)
"index_posts_on_category_id" btree (category_id)
(You can quit psql
with the command \q
.)
Now that our database is prepared, we can add the relationships to our models.
app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :category
app/models/category.rb
class Category < ActiveRecord::Base
has_many :posts
Try out those relationships in the console (which you'll need to restart).
Loading development environment (Rails 4.1.6)
[1] pry(main)> post = Post.first
Post Load (0.6ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1
=> #<Post id: 1, title: "TIL there's an immersive coding experience in Indy...", link: "https://elevenfifty.com/", body: "You should totally check this out. They have a T. ...", created_at: "2014-10-29 18:40:38", updated_at: "2014-11-05 04:03:21", post_type: 0, category_id: 1>
[2] pry(main)> category = Category.last
Category Load (0.3ms) SELECT "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT 1
=> #<Category id: 3, name: "todayilearned", title: "Today I Learned", description: "You learn something new every day; what did you le...", sidebar: "Submissions must be verifiable. Please link direct...", submission_text: "Please make sure your headline starts with 'TIL' a...", created_at: "2014-11-05 02:41:55", updated_at: "2014-11-05 02:41:55">
[3] pry(main)> post.category = category
=> #<Category id: 3, name: "todayilearned", title: "Today I Learned", description: "You learn something new every day; what did you le...", sidebar: "Submissions must be verifiable. Please link direct...", submission_text: "Please make sure your headline starts with 'TIL' a...", created_at: "2014-11-05 02:41:55", updated_at: "2014-11-05 02:41:55">
[4] pry(main)> post.save
(0.1ms) BEGIN
SQL (0.4ms) UPDATE "posts" SET "category_id" = $1, "updated_at" = $2 WHERE "posts"."id" = 1 [["category_id", 3], ["updated_at", "2014-11-05 04:08:08.596470"]]
(6.4ms) COMMIT
=> true
[5] pry(main)> post.reload
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT 1 [["id", 1]]
=> #<Post id: 1, title: "TIL there's an immersive coding experience in Indy...", link: "https://elevenfifty.com/", body: "You should totally check this out. They have a T. ...", created_at: "2014-10-29 18:40:38", updated_at: "2014-11-05 04:08:08", post_type: 0, category_id: 3>
[6] pry(main)> post.category
Category Load (0.3ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1 [["id", 3]]
=> #<Category id: 3, name: "todayilearned", title: "Today I Learned", description: "You learn something new every day; what did you le...", sidebar: "Submissions must be verifiable. Please link direct...", submission_text: "Please make sure your headline starts with 'TIL' a...", created_at: "2014-11-05 02:41:55", updated_at: "2014-11-05 02:41:55">
[7] pry(main)> category.posts
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."category_id" = $1 [["category_id", 3]]
=> [#<Post id: 1, title: "TIL there's an immersive coding experience in Indy...", link: "https://elevenfifty.com/", body: "You should totally check this out. They have a T. ...", created_at: "2014-10-29 18:40:38", updated_at: "2014-11-05 04:08:08", post_type: 0, category_id: 3>]
Are both sides of the relationship working? Let's take a moment to require that all posts have a category assigned to them. Add the following to the Post
model:
app/models/post.rb
validates :category_id, presence: true
You probably have some old post records that don't yet have categories assigned them. You can quickly edit them to assign categories, or do it in bulk via the console.
[1] pry(main)> Post.where(category: nil).each {|p| p.category = Category.first; p.save}
Commit!
$ git add .
$ git commit -m "Add many-to-1 relationship between Post and Category."