shou2017.com
JP

Creating a Blog with Rails

Wed Sep 13, 2017
Sat Aug 10, 2024

Objective

When posting a blog, I want to create a category at the same time. Categories will use pre-created ones. Here’s the image of what I want to achieve. I’m using Rails 5.

Creating a Blog with Rails

Since blogs and categories are separate, I’ll use accepts_nested_attributes_for to allow selecting a category while creating a blog.

Model

#blog.rb
class Blog < ApplicationRecord
  has_one :category, dependent: :destroy
  accepts_nested_attributes_for :category, allow_destroy: true
end

Category:

class Category < ApplicationRecord
  belongs_to :blog, optional: true
end

Controller

#BlogsController
def new
  @blog = Blog.new
  @blog.build_category
end

def blog_params
  params.require(:blog).permit(
    :title,
    :content,
    :editer,
    category_attributes: [:id, :name]
  )
end

View

Register the parent blog and the child category together. To apply Bootstrap classes to the select element, use the following:

<%= category.select :name, ['Gadgets', 'Parenting', 'Entertainment', 'IT', 'Overseas', 'Lifestyle'], { include_blank: "Please select" }, class: "form-control" %>

_form.html.erb

<%= form_for(blog) do |f| %>
  <div class="field">
    <%= f.label :Category %>
    <%= f.fields_for :category, @blog.category do |category| %>
      <%= category.select :name, ['Gadgets', 'Parenting', 'Entertainment', 'IT', 'Overseas', 'Lifestyle'], { include_blank: "Please select" }, class: "form-control" %>
    <% end  %>
  </div>

  <div class="field">
    <%= f.label :Title %>
    <%= f.text_field :title, class: "form-control", placeholder: "Title" %>
  </div>

  <div class="actions">
    <%= f.submit 'Create', class: "btn btn-primary btn-lg btn-block" %>
  </div>
<% end  %>

Table

#schema.rb
enable_extension "plpgsql"

create_table "blogs", force: :cascade do |t|
  t.string   "title"
  t.string   "content"
  t.string   "editer"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "categories", force: :cascade do |t|
  t.string   "name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.integer  "blog_id"
end

I tried creating it, and it worked successfully.

Creating a Blog with Rails

Show View

#blogs/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @blog.title %>
</p>

<p>
  <strong>Content:</strong>
  <%= @blog.content %>
</p>

<p>
  <strong>Category:</strong>
  <td><%= @blog.category.name %></td>
</p>

<p>
  <strong>Editor:</strong>
  <%= @blog.editer %>
</p>

<%= link_to 'Edit', edit_blog_path(@blog) %> |
<%= link_to 'Back', blogs_path %>

Initially, I thought about selecting categories from the database, but if the number of categories is small, this approach seems sufficient.

See Also