[Ruby] [Rails] Implement image posting function

3 minute read

Overview

While creating a bulletin board site in a Rails project, I had the opportunity to implement an image posting feature.
I will leave the procedure here as a memorandum.

1. Creating an Image model

Create Model and migration file

There are two tables, posts table and ʻimage table. The post_id` column of the image table has a relation to the post table.

terminal


$ rails g model Image image_url:string post:references

Open the created migration file and check it.

db/migrate/Date and time_create_images.rb


class CreateImages < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.string :image_url
      t.references :post, foreign_key: true

      t.timestamps
    end
  end

It seems that the migration file has been created without any problems.

Perform migration

terminal


$ rails db:migrate

Check Image Model

app/model/image.rb


class Image < ApplicationRecord
  belongs_to :post
end

** belongs_to: post ** is written because the references are set in the migration file.

Add relation to existing Post Model

Add ** has_many: images **.

app/models/post.rb


class Post < ApplicationRecord
  
  has_many :images #Add this line
end

With this, we were able to express a one-to-many relationship.

2. Use accepts_nested_attributes_for to enable updating of multiple models from one form

This time, I will register multiple models at once with one form so that images can be posted at the same time when posting a post.
What is needed for that

__ · accepts_nested_attributes_for__

is.
If you use this, you will be able to register has_many related child records at once.

Preparation for handling nested forms

You will create a nested form to register the tables together. Let’s use a method called accepts_nested_attributes_for to make that possible.

Now, let’s set accepts_nested_attributes_for.
Add the settings to the Post model as follows.

app/models/post.rb


class Post < ApplicationRecord
  
  has_many :images
  accepts_nested_attributes_for :images #Add this line
end

edit posts controller

Add the settings to posts_controller.rb as well.

app/controllers/posts_controller.rb


class PostsController < ApplicationController

  def new
    @post = Post.new
    @post.images.build #Add this line
  end

  def create
    @post = current_user.posts.build(post_params)
    if @post.save
      flash[:success] = 'I posted a message.'
      redirect_to root_url
    else
      @posts = current_user.feel_posts.order('created_at DESC').page(params[:page]).per(10)
      flash.now[:danger] = 'Failed to post the message.'
      render 'posts/new'
    end
  end

  private
  
  def post_params
    params.require(:post).permit(:content, :security, images_attributes: [:image_url]) #Add this line
  end

@post.images.build
And in params
images_attributes: [:image_url]
Was added.
The values of the parameters allowed by the nested model are described using images_attributes: in params.

Nest in a form using fields_for

erb:views/posts/new.html.erb


<%= form_for(@post) do |f| %>
  <div class="form-group">
    <%= f.label :content, 'comment' %>
    <%= f.text_area :content, class: 'form-control', rows: 5, placeholder: 'Please enter a comment'  %>
  </div>
        
  <div class="form-group">
    <%= f.label :security, 'Security_Level' %>
    <%= f.number_field :security, class: 'hoge', min: 0, max: 100 %>
  </div>
          
  <%= f.fields_for :images do |i| %>  #Add to this line
    <%= i.file_field :image_url %>
  <% end %>
                
  <div class="text-right">
    <%= f.submit 'Post', class: 'btn btn-primary' %>
  </div>
<% end %>

Now, you can register multiple models at once with one form.
Next, create an image upload function.

3. Install CarrierWave to allow you to upload images

Add the “Carrier Wave” gem to Gemfile.

Gemfile


gem 'carrierwave' #Image upload

Run bundle install.

terminal


$ bundle install

Creating an uploader

terminal


$ rails g uploader image

For image, set an appropriate name. This time, I named it image.
When you execute the command

create app/uploaders/image_uploader.rb

And the image uploader was created.

Model association

Add the following to /models/image.rb and specify the column name in mount_uploader.

app/models/image.rb


class Image < ApplicationRecord
  belongs_to :post
  
  mount_uploader :image_url, ImageUploader #Add to this line
end

The image model has a column called image_url, which is designed to store the URL of the image.
By specifying a column in mount_uploader, Carrierwave will automatically upload the image when updating the column and save the URL of the upload destination.

Display the image saved by Carrierwave in View

erb:view/posts/_posts.html.erb


 <div>
   <%= link_to post.user.name, user_path(post.user) %><span class="text-muted">posted at<% post.created_at %></span>
 </div>
 <div>
   <p><%= post.content %></p>
 </div>
 <div>
   <% post.images.each do |image| %>  
     <%= image_tag image.image_url.url %>  #Show here
   <% end %>
 </div>
 <div>
   <p>Security_Level <%= post.security %></p>
 </div>
        
 <div class="batton">
   <%= render 'favorites/favorite_button', post: post %>
   <% if current_user == post.user %>
     <%= link_to "Delete", post, method: :delete, data: { confirm: "Do you really want to delete this?" }, class: 'btn btn-danger btn-xs' %>
   <% end %>

The image posting function has been implemented.

At the end

You have now implemented the image posting function in your Rails project.
There are some points that can be improved, such as posting multiple images, but for the time being, it’s just a paragraph.

Thank you very much.