Rails accepts nested attributes and mongo error on delete - ruby-on-rails

I'm using models on Mongo and Rails' accepts_nested_attributes_for.
For specific scenerio, when I try to add attachment_ids in first item and also I remove second item I receive mongo error:
[40]: Updating the path 'section9.significant_events.0.attachment_ids' would create a conflict at 'section9.significant_events' (on localhost:27017, legacy retry, attempt 1)
Here are the params passed to controller:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"xxxxx",
"section9"=>
{
"significant_events_attributes"=>
{"0"=>{"_destroy"=>"false", "description"=>"", "info_location"=>"", "attachment_ids"=>["", "62f4bd4fba962ee7ae1e96fd"], "id"=>"62f4b604ba962ee7ae1e96bd"},
"1"=>{"_destroy"=>"1", "description"=>"", "info_location"=>"", "attachment_ids"=>[""], "id"=>"62f4b772ba962ee7ae1e96ca"}}
},
"commit"=>"Save",
"section_map"=>"section9"}
And the relations are:
Section9
embeds_many :significant_events
accepts_nested_attributes_for :significant_events, allow_destroy: true
SignificantEvent
has_and_belongs_to_many :attachments, class_name: 'Edoc'=
embedded_in :section9

I think field should appear either in $set, or in $setOnInsert. But it should not be in both.

Related

Validating presence of nested attributes returns error "no method :path_base"

I have a model which accepts nested attributes. There are 4 attributes altogether and I need to verify the presence of one. the specific attribute I need to verify for is called path_base so I tried
validates_presence_of :path_base
In the model but I am getting the error
undefined method `path_base' for #<Template:0x007fa279146360>
When saving the template record. The params getting sent look like this
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZO+Pi3/6WwNk0H3cFhgDbRywjrAOv2RnZ7olIsenND0=", "already_saved"=>"false", "update_pages"=>"false",
"template"=>{"type"=>"singleton", "name"=>"test",
"template_responses_attributes"=>{"0"=>{"path_base"=>"", "liquid_code"=>"test", "indexable"=>"1", "content_type"=>"text/html"}, "1"=>{"path_base"=>"", "liquid_code"=>"", "indexable"=>"1", "content_type"=>"text/html"}},
"template_fields_json"=>"[\r\n\r\n]"}, "button"=>""}
So inside the template_responses_attributes array is where the value of path_base is, and that is inside the template array just like normal (template is the controller/model that is saving the record that accepts the nested attributes).
If anyone could point me in the correct direction for this it would be greatly appreciated.
I did try this, which I found here but it did not return an error when the value was empty.
reject_if: proc { |attributes| attributes['path_base'].blank? }
Each model should only be responsible for validating its own attributes - if you want to ensure that the nested records are valid use validates_associated.
class Template < ApplicationRecord
has_many :responses
accepts_nested_attributes_for :responses
# This validates all the associated records
validates_associated :responses
end
class Response < ApplicationRecord
validates_presence_of :path_base
# ...
end
The reject_if option is not a validation mechanism. Rather it lets you filter out nested attributes if they do not meet a criteria, take for example a task list application where you would want to filter out the empty rows.

Rails has_many :through does not save relations or saves corrupted one

I have models:
class Agency < ActiveRecord::Base
has_many :specializations
has_many :cruise_lines, through: :specializations
end
class CruiseLine < ActiveRecord::Base
has_many :specializations
has_many :agencies, through: :specializations
end
class Specialization < ActiveRecord::Base
belongs_to :agency, inverse_of: :specializations
belongs_to :cruise_line, inverse_of: :specializations
end
In my view I have such part with checkboxes in the form in HAML
= agency.collection_check_boxes(:cruise_line_ids, CruiseLine.all, :id, :name) do |specialization|
.checkbox
= specialization.label do
= specialization.check_box type: "checkbox"
= specialization.object.name
This generates valid HTML ending with (it shows correctly already connected cruise lines to current agency - if I add needed entries directly to DB)
<div class='checkbox'>
<label for="agency_cruise_line_ids_27"><input type="checkbox" value="27" name="agency[cruise_line_ids][]" id="agency_cruise_line_ids_27" />
TransOcean Kreuzfahrten
</label>
</div>
<input type="hidden" name="agency[cruise_line_ids][]" value="" />
When saving form in log I see such params Parameters: {"utf8"=>"✓", "authenticity_token"=>"some-long-token", "agency"=>{"name"=>"Test agency", "website"=>"http://www.bla-bla.com", "description"=>"", "booking_email"=>"", "booking_phone"=>"", "optional_booking_phone"=>"", "working_hours"=>"", "cruise_line_ids"=>["1", "2", "15", "16", "27", "28", ""]}, "commit"=>"Update"}
Part that saves edited agency data and specialization (that is relation to CruiseLine)
attributes = params.require(:agency).permit(
:name, :website, :description, :booking_email, :booking_phone,
:optional_booking_phone, :working_hours, :cruise_line_ids
)
agency.specializations.build(attributes[:cruise_line_ids])
agency.update_attributes(attributes)
Agency data is saved without any problems, but in the specializations table is saved broken entry: agency_id with correct agency id, but cruise_line_id equals to empty string!!! Each time I save the form, one more corrupted relation is added to the existing ones.
Questions:
1) how to save relations in my case, cause current code simply does not work?
2) how to update those relations (that is in case if checkbox is unselected and form is saved, the unselected relations also should be removed)?
3) why does in params cruise_line_ids array's last element is empty string? And why is it generated by collection_ckeckbox_helper?
Please, help. I'm struggling with this 4 hours :(
The problem was in two places (incorrect building and redundant index in relations table):
I replaced this code
agency.specializations.build(attributes[:cruise_line_ids])
with the following:
attributes[:cruise_line_ids].each do |cruise_line_id|
if cruise_line_id.to_i > 0 # to skip last empty string element?!
agency.specializations.build(cruise_line_id: cruise_line_id)
end
end
and made small modification in the code (:cruise_line_ids => [] - this part) below:
attributes = params.require(:agency).permit(
:name, :website, :description, :booking_email, :booking_phone,
:optional_booking_phone, :working_hours, :cruise_line_ids => []
)
After that it started causing some PG::uniqueviolation error. It turned out to be a problem with compound unique index. Having examined the SQL log it turned out that my relations were saved twice (when building and the second time when updating attributes), so I put code that updated attributes before the block with building relations and everything became fine and perfect :)

Rails: restore ActiveRecord object encoded with to_json

I have a model called Account with these associations:
has_many :contracts, :dependent => :destroy
has_many :packages, :dependent => :destroy
accepts_nested_attributes_for :contracts
accepts_nested_attributes_for :packages
Before destroying any Account object i save it on a file with to_json:
#account.to_json(:include => [:packages, :contracts])
Fine. The problem happens when i try to restore it ( on another script ):
account_data = JSON.parse json
#account = Account.new account_data
This raises an exception:
Package(#70193553579560) expected, got Hash(#70193548333800)
Why this happens? Shouldn't Rails accept a hash in this case?
Will i have to remove contracts and packages keys from Hash and insert them after i do #account.save? I'm looking for a cleaner way to handle this :)
Look at the output of #account.to_json(:include => [:packages, :contracts]). It serializes the associations as JSON, so you end up with something like:
{"id":10, packages:[{id:5,description:"Package1"}], contracts:[]}
When you try to reload the JSON, under the hood it's trying to do this:
account.packages = [{id:5,description:"Package1"}]
This doesn't work because account.packages is an association, and you can't build it directly using a Hash. You can, however, pass the Hash as nested attributes:
account.packages_attributes = [{id:5,description:"Package1"}]
packages_attributes is a method defined by the accepts_nested_attributes_for class method, which you already have in your model.
This is not going to play nicely with to_json. However, ActiveRecord also has a from_json method that is designed to play nicely to to_json

Rails 3 - reject_if proc not catching blank fields

I'm getting rejected by reject_if.
The Item model has_many variants, with the model also accepting nested attributes:
accepts_nested_attributes_for :variants, :allow_destroy => :true,
:reject_if => :all_blank
When I submit, it posts the following parameters:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"66areo4waM82H66771RkUD/Zt3rrp8Hgk/mwOqV42YI=", "item"=>{"name"=>"Dans", "body"=>"adsdsa", "visible"=>"1", "sellable"=>"0", "variants_attributes"=>{"0"=>{"name"=>"", "price"=>"", "qty"=>"", "sku"=>"", "_destroy"=>"false"}}}, "commit"=>"Save Item", "id"=>"6"}
For reference the controller:
def edit
#item = Item.find(params[:id])
#item.variants.build
The variants attributes are blank, but they aren't being rejected by the item model. So all the validations come through, making it unable to save. Any ideas?
Calling build on association doesn't have anything to do with reject_if options of accepts_nested_attributes_for. You call build without any parameters so it initializes variant with default attributes values.
Using reject_if matter when you initialize or update your parent model like that passing attributes for association models (variants) directly to parent model (item):
item = Item.new :name => "Dans", :variants_attributes => { "0" => { :name => "" } }
If reject_if is false you'll get new item with one variant having empty name. If reject_if is true this variant won't be created as all attributes passed are blank. But using this option do not prevent you from creating variants with blank attributes manually using build or directly adding variants to the item like that:
item.variants << Variant.new
Btw, why do you call build in your edit action? Usually edit action just fetches the model and renders the form. Updating attributes of a model happens in update action like that:
#item = Item.find params[:id]
#item.update_attributes params[:item]

Can't mass-assign protected attributes

Updating the code formatting for better viewing.
Folks,
I have been looking at this for sometime but I don't understand what could be messing up here. I am using Devise.
class User < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses
# Other stuff here
end
class Address < ActiveRecord::Base
belongs_to :user
validates_presence_of :zip #:street_address1,
end
-------------------- log output begin ------------------------------
Started POST "/users" for 127.0.0.1 at
2011-05-28 11:43:27 -0700 Processing
by RegistrationsController#create as
HTML Parameters: {"utf8"=>"√",
"authenticity_token"=>"CEmdqlsmdYa6Jq0iIf5KAxxISsUCREIrFNXWkP80nhk=",
"user"=>{"email"=>"a2#gmail.com",
"password"=>"[FILT ERED]",
"addresses_attributes"=>{"0"=>{"street_address1"=>"234
Pitkin Ct.", "zip"=>"12456"}}},
"commit"=>"Sign up"} WARNING: Can't
mass-assign protected attributes:
addresses_attributes SQL (0.0ms)
BEGIN SQL (164.0ms) SHOW TABLES
User Load (0.0ms) SELECT users.id
FROM users WHERE (users.email =
BINARY 'a2#gmail.com') LIMIT 1 SQL
(1.0ms) ROLLBACK
-------------------- log output end ------------------------------
The zip is present in the data posted and the posted data seems to be formatted properly. On the web page form I am getting the error that "Addresses zip can't be blank". I have dug around for what causes the "Can't mass-assign protected attributes" warning but haven't found anything that will help me.
Thanks for your thoughts and pointers.
-S
Have a look here and learn :)
http://railscasts.com/episodes/26-hackers-love-mass-assignment
Edit:
Having accepts_nested_attributes_forin User model enables you to send the data to the Address model.
Then, in the Address model, you have to set the requested attr_accessible
Inside of SpecificModel (appfolder/app/model/specific_model.rb)
Try using
attr_accessible :addresses_attributes, :another_attribute_to_make_mass_assignable, :another_attribute, etc.
Nowadays (April 2013) you should start to use https://github.com/rails/strong_parameters
Just include the datafield in the model as mentioned below
attr_accessible :addresses_attributes

Resources