Chris Mikkelsen
Chris Mikkelsen

Reputation: 4087

How to update Model attribute in Rails

SO I have this app, that let's users order shirt online.

This is how the item order looks, it has shirt size, shirt color, phone number, address, and order status.

enter image description here

I was modifying the existing code so that the admin users can change the order status from "ordered" into "processing", or "delivered" by selecting an option from a dropdown list below the order status.

Right now I'm not sure if I'm doing it right. It's giving me an error when I click on the update link, and it's highlighting a code in my controller

enter image description here

SO this is my Model

class Micropost < ApplicationRecord
   belongs_to :user
   default_scope -> { order(created_at: :desc) }
   mount_uploader :picture, PictureUploader
   validates :user_id, presence: true
   validates :shirtSize, presence: true
   validates :shirtColor, presence: true
   validates :contactAddress, presence: true
   validates :contactNumber, presence: true
   validate  :picture_size
   #uncomment picture presence validation in the future
   # validates :picture, presence: true

   SIZE_LIST = [ " ", "S", "M", "L", "XL" ]
   COLOR_LIST = [ " ", "Black", "White", "Gray", "Red", "Green", "Blue", "Navy Blue", "Yellow", "Pink"]
   STATUS_LIST = [ "Ordered", "Processing", "Delivered"]

   private

   # Validates the size of an uploaded picture.
   def picture_size
       if picture.size > 5.megabytes
          errors.add(:picture, "should be less than 5MB")
       end
   end
 end

VIEW

<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %> 
</span>
<span class="content">
<%= "#{micropost.shirtSize}, #{micropost.shirtColor}" %>
<% if current_user?(micropost.user) || current_user.admin? %><br/>
  <%= "#{micropost.contactNumber} | #{micropost.contactAddress}" %> 
  <br/>
  <h4>Status: <%= micropost.orderStatus %></h4>
<% end %>

//dropdown list for changing order status that is only available for admin users
<% if current_user.admin? %><br/>
  <%= form_for(@micropost) do |f| %>
    <div class="field">
      <%= f.select :orderStatus, Micropost::STATUS_LIST %>
      <%= link_to "Update", micropost, method: :patch %>
    </div>
  <% end %>
<% end %>

</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) && micropost.orderStatus == "Ordered" %>
<%= link_to "Cancel", micropost, method: :delete,
                                 data: { confirm: "You sure?" } %>
<% end %>
</span>
<span class="content">
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
</li>

CONTROLLER

class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :update]
before_action :correct_user,   only: :destroy
before_action :admin_user,     only: :update

def create
  @micropost = current_user.microposts.build(micropost_params)
  if @micropost.save
    flash[:success] = "Design Posted!"
    redirect_to root_url
  else
    @feed_items = []
    render 'static_pages/home'
  end
end

def destroy
  @micropost.destroy
  flash[:success] = "Design deleted"
  redirect_to request.referrer || root_url
end

def update
@micropost = Micropost.find(params[:id])
  if @micropost.update(update_micropost_params)
    flash[:success] = "Micropost updated"
    redirect_to @micropost
  else
    flash[:error] = 'There was a problem updating this micropost'
    render :edit
  end
end

private

  def micropost_params
    defaults = { orderStatus: 'Ordered' }
    params.require(:micropost).permit(:shirtSize, :shirtColor, :contactNumber, :contactAddress, :picture).merge(defaults)
  end

  def update_micropost_params
    params.require(:micropost).permit(:shirtSize, :shirtColor, :contactNumber, :contactAddress, :picture, :orderStatus)
  end

  def correct_user
    @micropost = current_user.microposts.find_by(id: params[:id])
    redirect_to root_url if @micropost.nil?
  end

  # Confirms an admin user.
  def admin_user
    redirect_to(root_url) unless current_user.admin?
  end

end

ROUTES

Rails.application.routes.draw do
root   'static_pages#home'
get    '/help',    to: 'static_pages#help'
get    '/about',   to: 'static_pages#about'
get    '/contact', to: 'static_pages#contact'
get    '/signup',  to: 'users#new'
get    '/login',   to: 'sessions#new'
post   '/login',   to: 'sessions#create'
delete '/logout',  to: 'sessions#destroy'
resources :users do
  member do
    get :following, :followers
  end
end
resources :account_activations, only: [:edit]
resources :password_resets,     only: [:new, :create, :edit, :update]
resources :microposts,          only: [:create, :destroy, :update]
resources :relationships,       only: [:create, :destroy]
end

Upvotes: 2

Views: 300

Answers (3)

Chris Mikkelsen
Chris Mikkelsen

Reputation: 4087

I figured it out, I changed the update method in my controller

def update
  @micropost = Micropost.find(params[:id])
  status = 'Ordered'
  if @micropost.orderStatus == 'Ordered'
    status = 'Processing'
  elsif @micropost.orderStatus == 'Processing'
    status = 'Delivered'
  end
  @micropost.update_attributes(orderStatus: status)
  if @micropost.save
    flash[:notice] = "Entry was successfully updated"
    redirect_to request.referrer || root_url
  else
    flash[:error] = 'There was a problem updating this micropost'
    redirect_to request.referrer || root_url
  end
end

I also updated my view into something like this:

<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
  <%= "#{micropost.shirtSize}, #{micropost.shirtColor}" %>

  <% if current_user?(micropost.user) || current_user.admin? %><br/>
    <%= "#{micropost.contactNumber} | #{micropost.contactAddress}" %><br/>
    <h4>Status: <%= micropost.orderStatus %></h4>
  <% end %>

  <% if current_user.admin? %>
    <%= link_to "Update", micropost, method: :patch %>
  <% end %>

</span>
<span class="timestamp">
  Posted <%= time_ago_in_words(micropost.created_at) %> ago.
  <% if current_user?(micropost.user) && micropost.orderStatus == "Ordered" %>
    <%= link_to "Delete", micropost, method: :delete,
                                 data: { confirm: "You sure?" } %>
  <% end %>
</span>
<span class="content">
  <%= image_tag micropost.picture.url if micropost.picture? %>
</span>

Upvotes: 2

John Hinnegan
John Hinnegan

Reputation: 5972

Reading the comments on this file will show you what is generated by the scaffold generator. This can be thought of as default controller implementation, which includes an update method.

Upvotes: 0

hashrocket
hashrocket

Reputation: 2222

Normally, for an update action, you would do something like this:

def update
  if admin_user
    @micropost = Microposts.find(params[:id]) # you may need to adjust this if your route is nested

    if @micropost.update(micropost_params)
      flash[:success] = "Micropost updated"
      redirect_to @micropost
    else
      flash[:error] = 'There was a problem updating this micropost'
      render :edit
    end
  end
end

This will probably have to be adjusted since you are using an admin user to make the update.

Upvotes: 1

Related Questions