1. Generating Model and Controller for Posts

For the next few lessons, we're going to be setting up the backend logic for user posts. We'll take it step by step, so you won't get lost.

The first thing we want to do is generate our controller and model for our Post object. We can name the object whatever we want, but in this case, let's name it Post.

Naming Objects

When naming objects, make sure that the naming makes sense. For example, in our app, we need an object that will represent a post created by a user. Therefore, naming the object "Post" makes total sense.

However, if we name it something random like "Almond", it starts making no sense at all. You'll be running commands like Almond.new or Almond.all for trying to create or show user posts, which really makes no sense in this case. Instead, Post.new or Post.all would make much more sense

First, let's generate the controller. We can do this by running the following command:

rails generate controller posts

Notice how we generated a "posts" controller and not a "post" controller. If we recall, by rails naming conventions, controller names must be plural in general, and model names must be singular. If you don't follow these rules, unexpected errors might pop up.

Next, let's go ahead and generate our model post with fields photo and description which belongs to the user.

rails g model post description:text user:references

This will generate a migration file where we can specify the database columns that we want to add to the Post object.

The migration file will be a file named something like xxxxxxxxxxx_create_posts.rb.

Let's see what database columns we need to add for the Post object.

If we look back at our wire frames, we see that Post will have a description. So we need to add these columns. Let's go ahead and add this to the migration file.

class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.text :description
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
  end
end

Then, on running the migration using rails db:migrate. This migration will take care of adding a new column named user_id to posts table (referencing id column in users table) and it will also add an index on the new column.

The schema after migration looks as follows:

...
create_table "posts", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
  t.text "description"
  t.bigint "user_id", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["user_id"], name: "index_posts_on_user_id"
end
...

Notice that description is a text. The difference between string and text is in the size, string should be used for shorter text, and text should be used for longer text. See this Stackoverflow post for an in-depth explanation.

The above is an easy way to add relationship between users and posts. It will also add a belongs_to relationship with user. Another way is first creating the model and then adding the relationship. For the second way after creating a model we add the index.

rails g model post description:text

We need to do is to set up an index for the user_id column. Let me explain what this means.

Indexing

Indexing is super important in order to maintain your rails app performance. Here's an example of how it works.

Imagine you have a list of all the words in the English language, without any labels (A~Z) whatsoever. Then, you are given a word, for instance, "Cat". Your task is to find the word "Cat" from this list.

Obviously, this would be a huge task. You would have to look through each of the words to see if there is a match.

However, let's say you have a dictionary. A dictionary has labels which allows you to skip to the letter "C", and search for the word "Cat" within that range. This significantly decreases the amount of words you have to go through.

This is essentially what indexing is all about. It makes looking up things much more efficient.

So when should you use indexes?

As a rule of thumb, you should always use indexes when you have a foreign key.

What is a foreign key?

A foreign key is a value that belongs to another object. For instance, in this case, user_id is a foreign key, since it references the id of another User object from the Post object.

For a more in-depth look into indexing and how it improves performance, check out this blog post.

That means we need to add an index for the user_id in our Posts. Fortunately, this is quite simple. All we need to do is add add_index :posts, :user_id after the change method.

Your create_posts.rb file should now look like this:

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.integer :user_id
      t.text :description
      t.timestamps null: false
    end
    add_index :posts, :user_id
  end
end

Finally, run rails db:migrate to migrate the columns.

Now, for storing photos, let's add has_one_attached :photo to the Post model. The Post model should look something:

image.png

Lesson list