Rails 4接受has_many通过关系接受_ented_attributes_for :: _destroy不起作用

时间:2015-03-24 05:17:10

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

在Rails 4.1.6应用程序中:

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

详细说明: 按照https://github.com/nathanvda/cocoon/wiki/A-guide-to-doing-nested-model-forms关于设置has_many通过关系的描述,我最终得到了这段代码:

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

甲板和信标之间有类似的has_many关系:

beacon.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

在decks_controller的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和强参数正确...但也许不是,因为这是我能想到的唯一不正确的东西。

1 个答案:

答案 0 :(得分:0)

你可以直接删除父母,以便孩子也被删除。你可以直接删除孩子......比如

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

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