【问题标题】:Rails 4 accepts_nested_attributes_for for has_many through relationship: :_destroy is not workingRails 4 通过关系为 has_many 接受_nested_attributes_for::_destroy 不起作用
【发布时间】:2015-03-24 05:17:10
【问题描述】:

在 Rails 4.1.6 应用程序中:

简短说明:_destroy 属性设置为 true 或 1 时,不会删除 has_many through 关系中的关联。

详情: 按照https://github.com/nathanvda/cocoon/wiki/A-guide-to-doing-nested-model-forms 对建立has_many through 关系的描述,我最终得到了这段代码:

deck.rb:

class Deck < ActiveRecord::Base
  belongs_to :beacon_owner

  # Beacon relationship.
  has_many :deck_pointers, dependent: :destroy
  has_many :beacons, through: :deck_pointers

  accepts_nested_attributes_for :deck_pointers, allow_destroy: true

  # Card relationship.
  has_many :card_pointers, dependent: :destroy
  has_many :cards, through: :card_pointers

  accepts_nested_attributes_for :cards
  accepts_nested_attributes_for :card_pointers, allow_destroy: true

  validates :name, presence: true, uniqueness: true
end

card.rb:

class Card < ActiveRecord::Base
  belongs_to :beacon_owner
  belongs_to :template

  # Deck relationship.
  has_many :card_pointers, dependent: :destroy
  has_many :decks, through: :card_pointers
  ...
end

card_pointer.rb:

class CardPointer < ActiveRecord::Base
  belongs_to :card
  belongs_to :deck

  accepts_nested_attributes_for :card, reject_if: :all_blank
end

deck 和 beacons 之间也有类似的 has_many through 关系:

信标.rb:

class Beacon < ActiveRecord::Base
  belongs_to :beacon_owner
  has_many :deck_pointers, dependent: :destroy
  has_many :decks, through: :deck_pointers

  accepts_nested_attributes_for :deck_pointers, allow_destroy: true
  ...
end

deck_pointer.rb:

class DeckPointer < ActiveRecord::Base
  belongs_to :beacon
  belongs_to :deck

  accepts_nested_attributes_for :beacon, reject_if: :all_blank
end

在甲板控制器的 RSpec 测试中,POST 和 PUT 似乎正确更新 - 但当我尝试删除关联时 PUT 失败。阅读http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html,我知道使用 _destroy 属性,但它不起作用。

我的强大参数 - 我的 decks_controller 中的 deck_params 可能是错误的,但我不确定可能需要更改什么:

class API::V1::DecksController < API::BaseController
  before_filter :authenticate_user_from_token!, except: [:show]
  before_action :set_deck, only: [:show, :update, :destroy]
  ...
  def create
    @deck = Deck.new(deck_params)
    @deck.beacon_owner_id = current_beacon_owner.id
    @deck.save
    respond_with(@deck)
  end

  def update
    @deck.update(deck_params)
    respond_with(@deck)
  end
  ...

  private
    def set_deck
      @deck = Deck.find(params[:id])
    end

    def deck_params
      params.require(:deck).permit(:name,
        deck_pointers_attributes: [:id, :beacon_id, :deck_id, :_destroy],
        card_pointers_attributes: [:id, :card_id, :deck_id, :_destroy])
        # {deck_pointers_attributes: [:id, :beacon_id, :deck_id, :_destroy]},
        # {card_pointers_attributes: [:id, :card_id, :deck_id, :_destroy]})
    end
end

我的测试是这样写的:

    it "updates the deck with fewer cards and beacons, deleting card/deck pointers" do
      deck = FactoryGirl.create :deck, name: 'Deck 1', beacon_owner_id: current_beacon_owner.id
      beacon1 = FactoryGirl.create :beacon, name: 'Beacon 1', uuid: 'abcd', beacon_owner_id: current_beacon_owner.id
      beacon2 = FactoryGirl.create :beacon, name: 'Beacon 2', uuid: 'efgh', beacon_owner_id: current_beacon_owner.id
      card1 = FactoryGirl.create :card, title: 'Card 1', beacon_owner_id: current_beacon_owner.id
      card2 = FactoryGirl.create :card, title: 'Card 2', beacon_owner_id: current_beacon_owner.id

      FactoryGirl.create :deck_pointer, deck_id: deck.id, beacon_id: beacon1.id
      FactoryGirl.create :deck_pointer, deck_id: deck.id, beacon_id: beacon2.id
      FactoryGirl.create :card_pointer, deck_id: deck.id, card_id: card1.id
      FactoryGirl.create :card_pointer, deck_id: deck.id, card_id: card2.id

      assert Deck.find_by_id(deck.id).cards.count == 2
      assert Deck.find_by_id(deck.id).beacons.count == 2
      assert CardPointer.count == 2
      assert DeckPointer.count == 2

      # TODO - why doesn't this change?
      puts '-----------------------------------------'
      puts "card pointer count: #{CardPointer.count}"
      puts "deck pointer count: #{DeckPointer.count}"
      puts "deck.cards.count: #{Deck.find_by_id(deck.id).cards.count}"
      puts "deck.beacons.count: #{Deck.find_by_id(deck.id).beacons.count}"
      puts '-----------------------------------------'

      put :update, format: :json, id: deck.id, deck: {
        name: "Updated Deck",
        card_pointers_attributes: [
          { card_id: card1.id, _destroy: true },
        ],
        deck_pointers_attributes: [
          { beacon_id: beacon1.id, _destroy: true },
          { beacon_id: beacon2.id, _destroy: true }
        ]
      }
      puts '-----------------------------------------'
      puts "card pointer count: #{CardPointer.count}"
      puts "deck pointer count: #{DeckPointer.count}"
      puts "deck.cards.count: #{Deck.find_by_id(deck.id).cards.count}"
      puts "deck.beacons.count: #{Deck.find_by_id(deck.id).beacons.count}"
      puts '-----------------------------------------'
      assert Deck.find_by_id(deck.id).cards.count == 1
      assert Deck.find_by_id(deck.id).beacons.count == 0

      assert CardPointer.count == 1
      assert DeckPointer.count == 0
   end

我是否在概念上误解了某些东西?我认为我的 allow_destroy 和 strong 参数是正确的……但也许不是,因为这是我能想到的唯一不正确的事情。

【问题讨论】:

  • 好吧,没关系 - 我只是手动实现的。

标签: ruby-on-rails rspec foreign-key-relationship


【解决方案1】:

您可以直接删除父级,以便子级也被删除。否则您可以直接删除子级...例如

@user=User.first
//get any random client
client=Client.find 20
//delete child directly
@user.clients.delete(client)

此外...您需要检查父/子是否都存在,因为每次测试完成后测试数据库都会重置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 2011-01-13
    • 2013-09-29
    相关资源
    最近更新 更多