Sasha
Sasha

Reputation: 6466

Rails Custom Nested Update not updating

My app has an Animal model which has many (and accepts nested attributes for) Packages. In my Animals Controller, I created the following action to allow for the editing of certain Animal (and nested Package) attributes in a separate page (and for a separate user role) from that used for normal Animal editing. (To clarify, I have an animals#edit action and page, and a separate animals#log action and page, which ideally allow different user roles to edit different things about the animal):

def log
    @animal = Animal.find(params[:animal_id])

    if params[:animal]
      if @animal.update_attributes(params[:animal])
        # I want a basic reload (with flash) in cases of both success and failure.
        redirect_to log_path(@animal), notice: "Animal update successful!"
      else
        redirect_to log_path(@animal), notice: "Update unsuccessful. Contact admin"
      end
    end
end

My separate edit/update actions:

  def update
    @animal = Animal.find(params[:id])

    if @animal.update_attributes(params[:animal])
        redirect_to @animal, notice: "Animal was successfully updated."
    else
        render action: "edit"
    end
  end

def edit
    @animal = Animal.find(params[:id])
end

The problem is, when I hit my submit button in my log page (code below), it refreshes the page, but a) without any notice of any sort (minor problem), and b) more importantly, without actually updating the package information. Importantly, if I update the animal.weight attribute (the only ANIMAL attribute modifiable here), THAT works. Just not the nested package attributes.

Help? I'm really stumped here. I've been fiddling with this forever trying to get it to work. Some possibly relevant code below:

In my routes.rb file (above animal resources):

match '/animals/:animal_id/log' => "animals#log", as: :log

Here's my log view:

<%= form_for(@animal, :url => { :action => "log" }, :method => :put) do |f| %>  

    <div style="width: 200px">
        <% if @animal.weight %>
            <%= "Weight: #{@animal.weight}lbs" %>
        <% else %>
            <%= f.label "Weight (in lbs)" %>
            <%= f.text_field :weight %>
        <% end %>
    </div>

    # I'm cycling through each bundle to group "identical" packages in a table (one per bundle).

    <% @animal.sold_bundles.each do |bundle| %> <!-- Packages are bundled by cut and prep notes -->
      <%= render partial: 'package_bundle', locals: {:bundle => bundle, :f => f} %><br>
    <% end %>

  <%= f.submit "Submit Animal Log", :class => "btn image-button right" %>

<% end %>

And here's the "package_bundle" partial called for each bundle. It produces a table of packages, some editable (because of blank true_weight value) and some not:

<table class="table">
    <thead>
        <tr>
            <th>Cut Name</th>
            <th>Prep Notes</th>
            <th>Label</th>              
            <th>Actual Lbs</th>
            <th>Actual Oz</th>
        </tr>
    </thead>
    <tbody>
        <!-- list of specified packages -->
            <%= f.fields_for :packages, bundle do |p| %>
                    <tr>
                        <td><%= p.object.name %></td>
                        <td><%= "#{p.object.user.name.first(3).upcase}-#{p.object.id}" %></td>
                        <% if p.object.true_weight == nil || p.object.true_weight == 0 %>
                            <td colspan=1><%= p.text_field :actual_lbs %></td>
                            <td colspan=1><%= p.text_field :actual_oz %></td>
                        <% else %>
                            <td><%= p.object.true_weight.round(0) %> lb</td>
                            <td><%= ((p.object.true_weight % 1) * 16).round(2)%> oz </td>
                        <% end %>  
                    </tr>   
            <% end %>  

    </tbody>
</table>

EDIT -- More code on request

package.rb (just what's relevant) -- I evaded the below gotcha (this time; I've definitely made that mistake before). It's a possibility that my method for converting lbs/oz to lbs with percent is to blame here, but it looks right to me:

class Package < ActiveRecord::Base
  attr_accessible :animal_id, :cut_id, :price, :line_id, :sold, :savings, :actual_lbs, :actual_oz, :true_weight
  attr_accessor :actual_lbs, :actual_oz
  belongs_to :animal
  belongs_to :cut
  belongs_to :line
  before_update :to_true

def to_true
    if self.sold
      if self.order.status > 1 # This is the case for any package loaded on the log page
        if self.actual_lbs && self.actual_oz
          unless self.true_weight && self.true_weight > 0 # Don't overwrite legit true weights
            percent = self.actual_oz / 16
            self.update_attribute(:true_weight, self.actual_lbs + percent)
          end
        end
      end
    end
  end

animal.rb (only relevant):

class Animal < ActiveRecord::Base
  attr_accessible :breed, :name, :photo, :animal_type, :hanging_weight, :meat_weight,  
                  :weight, :ranch_id, :butcher_id, :cow_mult, :pig_mult, :packages_attributes,
                  :lamb_mult, :goat_mult, :host_id, :final_sale, :opening_sale, :open, :no_sales
  has_many :orders
  has_many :packages
  belongs_to :butcher
  belongs_to :ranch
  belongs_to :host
  after_create :create_packages

  accepts_nested_attributes_for :packages

Looked at my server log (easier for me to understand than the dev log), to get to the bottom of this, and notice two weird things. There's no error at all, but 1) It seems to execute the GET for the page when I load it, but not the requested PUT when I hit submit, and 2) Probably as a consequence, the only param passed (which is passed as part of the GET request) is the animal id; no package attributes are passed.

Upvotes: 0

Views: 247

Answers (2)

RyanJM
RyanJM

Reputation: 7068

attr_accessor won't trigger callbacks. You have to make a variable "dirty". More info here.

In your package.rb:

def actual_lbs=(val)
  true_weight_will_change!
  @actual_lbs=val
end

This will make it such that when it sets the actual_lbs variable, it will "dirty" a variable that is in the database, thus firing the callbacks.

Upvotes: 1

kries
kries

Reputation: 199

Can you post your model code? A common "gotcha" that comes to mind is not adding, in your case, package_attributes to the attr_accessible on the animal model. It will not raise an error, but tucked into the development log, prior to the SQL entries, you will see the mass-assignment warning.

Upvotes: 2

Related Questions