开发者

nested mass assignment with mongoid

开发者 https://www.devze.com 2023-03-01 10:15 出处:网络
with a has_one/belongs_to relationship, i cannot seem to update nested records via mass assignment. models:

with a has_one/belongs_to relationship, i cannot seem to update nested records via mass assignment.

models:

class ProductVariation
  include Mongoid::Document
  has_one                       :shipping_profile,  :inverse_of => :variation
  field                         :quantity
  attr_accessible               :shipping_profile_attributes
  accepts_nested_attributes_for :shipping_profile
end

class ShippingProfile
  include Mongoid::Document
  belongs_to       :variation, :class_name => "ProductVariation"
  field            :weight,   开发者_开发知识库 :type => Float
  attr_accessible  :weight
end

controller:

@variation = ProductVariation.find(params[:id])
@variation.update_attributes(params[:product_variation])

post request:

Parameters:{
  "product_variation"=>{
    "quantity"=>"13",
    "shipping_profile_attributes"=>{
      "weight"=>"66",
      "id"=>"4dae758ce1607c1d18000074"
    }
  },
  "id"=>"4dae758ce1607c1d18000073"
}

mongo query:

MONGODB app_development['product_variations'].update({"_id"=>BSON::ObjectId('4dae758ce1607c1d18000073')}, {"$set"=>{"quantity"=>13, "updated_at"=>2011-04-28 06:59:17 UTC}})

and i dont even get a mongo update query if the product_variation doesnt have any changed attributes... what am i missing here?


Working models and a unit test are below to demonstrate that you can update child parameters and save to the database through the parent as intended via accepts_nested_attributes_for and the autosave: true option for relations.

The Mongoid documentation says that an error will be raised for an attempt to set a protected field via mass assignment, but this is out of date. Instead, messages like the following are printed to the log file.

WARNING: Can't mass-assign protected attributes: id

You should carefully look for for these messages in the appropriate log file to diagnose your problem. This will help you notice that you have a nested id field for the shipping profile in your parameters, and this seems to cause the weight to be rejected as well, probably along with all child parameters. After adding "attr_accessible :id" to the ShippingProfile model, the weight now gets assigned. You also need to add "attr_accessible :quantity" (and I've added :id for the unit test) to the ProductVariation model

The next issue is that you need "autosave: true" appended to the has_one relation in order to have the child updated through the parent, otherwise you will have to save the child manually.

You might also be interested in sanitize_for_mass_assignment, which can be used to launder out ids.

include ActiveModel::MassAssignmentSecurity

p sanitize_for_mass_assignment(params['product_variation'], :default)

The unit test should make the whole subject clear, I'll leave the controller work to you. Hope that this is clear and that it helps.

class ProductVariation
  include Mongoid::Document
  has_one                       :shipping_profile, :inverse_of => :variation, autosave: true
  field                         :quantity
  accepts_nested_attributes_for :shipping_profile
  attr_accessible               :id
  attr_accessible               :quantity
  attr_accessible               :shipping_profile_attributes
end

class ShippingProfile
  include Mongoid::Document
  belongs_to       :variation, :class_name => "ProductVariation"
  field            :weight,    :type => Float
  attr_accessible  :id
  attr_accessible  :weight
end

test/unit/product_varitation_test.rb

require 'test_helper'

class ProductVariationTest < ActiveSupport::TestCase
  def setup
    ProductVariation.delete_all
    ShippingProfile.delete_all
  end

  test "mass assignment" do
    params = {
      "product_variation"=>{
        "quantity"=>"13",
        "shipping_profile_attributes"=>{
          "weight"=>"66",
          "id"=>"4dae758ce1607c1d18000074"
        }
      },
      "id"=>"4dae758ce1607c1d18000073"
    }

    product_variation_id = params['id']
    shipping_profile_id = params['product_variation']['shipping_profile_attributes']['id']
    product_variation = ProductVariation.create("id" => product_variation_id)
    shipping_profile = ShippingProfile.create("id" => shipping_profile_id)
    product_variation.shipping_profile = shipping_profile
    assert_equal(1, ProductVariation.count)
    assert_equal(1, ShippingProfile.count)

    product_variation.update_attributes(params['product_variation'])
    assert_equal('13', ProductVariation.find(product_variation_id)['quantity'])
    assert_equal(66.0, ShippingProfile.find(shipping_profile_id)['weight'])
    p ProductVariation.find(product_variation_id)
    p ShippingProfile.find(shipping_profile_id)
  end
end

test output

Run options: --name=test_mass_assignment

# Running tests:

#<ProductVariation _id: 4dae758ce1607c1d18000073, _type: nil, quantity: "13">
#<ShippingProfile _id: 4dae758ce1607c1d18000074, _type: nil, variation_id: BSON::ObjectId('4dae758ce1607c1d18000073'), weight: 66.0>
.

Finished tests in 0.014682s, 68.1106 tests/s, 272.4424 assertions/s.

1 tests, 4 assertions, 0 failures, 0 errors, 0 skips

Process finished with exit code 0
0

精彩评论

暂无评论...
验证码 换一张
取 消