Reputation: 6466
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
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
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