删除记录不会破坏关联

时间:2018-01-30 09:52:55

标签: activerecord ruby-on-rails-5

我有Task模型和User模型,我有一个联接表b / w Task& User名为Tag。 Task.rb

class Task < ActiveRecord::Base
  belongs_to :user
  has_many :comments, dependent: :destroy
  has_many :tags, dependent: :destroy
  validates :description, presence: true

  def tagged_user_ids
    tags.map{|tag| tag.user_id}
  end

  def tag_exists_for(user)
    tagged_user_ids.include?user.id.to_s
  end

  def tag(user)
    tags.create(user_id: user.id)
  end

  def untag(user)
    tags.find_by(user_id: user.id).destroy
  end
end

User.rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :tasks
  has_many :tags
  has_many :comments,through: :tasks

  def tagged_tasks
    tags.map{|tag| tag.task}
  end

  def all_tasks
    self.tasks + self.tagged_tasks
  end

end

Tag.rb

class Tag < ActiveRecord::Base

  belongs_to :user
  belongs_to :task

end

我为untag方法编写了以下测试用例

describe "untag" do
    it "should untag user from task" do
      create_task_and_tag_user
      p @task2.tags
      @task2.untag(@user)
      p @task2.tags
      expect(@task2.tag_exists_for(@user)).to be false 
    end  
  end

def create_task_and_tag_user
    @user = User.create(email:"asd@jaka.com",password: 123456,name: "user1")
    @task1 = @user.tasks.create(description: "some description")
    @user2 = User.create(email:"user2@jaka.com",password: 123456,name: "user2")
    @task2 = @user2.tasks.create(description: "some other task")
    @task2.tag(@user)
  end

但此测试失败

Task untag should untag user from task
     Failure/Error: expect(@task2.tag_exists_for(@user)).to be false

       expected false
            got true

在进一步调查中,我发现untag(tag)删除了标记记录,但是当我调用task.tags时,它会显示在collectionproxy中

Tag.find_by(id: 39)
  Tag Load (0.3ms)  SELECT  `tags`.* FROM `tags` WHERE `tags`.`id` = 71 LIMIT 1
 => nil
2.4.1 :042 > t1.tags
 => #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 39, user_id: "3", task_id: "37", created_at: "2018-01-18 11:13:23", updated_at: "2018-01-18 11:13:23">, #<Tag id: 43, user_id: "9", task_id: "37", created_at: "2018-01-22 04:56:13", updated_at: "2018-01-22 04:56:13">, #<Tag id: 44, user_id: "5", task_id: "37", created_at: "2018-01-22 05:31:16", updated_at: "2018-01-22 05:31:16">]>
2.4.1 :043 >

我来自Mongodb,nosql背景,所以从未经历过这种行为。 有没有办法我可以删除关联(不使用任何宝石,一些建议使用偏执宝石,并用really_destroy替换销毁!)

1 个答案:

答案 0 :(得分:1)

这是因为您销毁的Tag对象仍然在ruby中缓存。这就是reload解决问题的原因,因为它从数据库中重新获取标签。

为了提高性能而不必在测试中调用reload,我建议用这种方式重写方法tagged_user_ids

def tagged_user_ids
  tags.pluck(:user_id)
end

首先,这将调用SQL查询仅提取user_id列而不是所有列。其次,对此方法的每次调用都将执行一个新的SQL查询,在这种情况下,如果删除某些标记,此方法将反映结果而不在任务对象上调用reload

另一个不相关的改进是将tagged_tasks重写为:

has_many :tagged_tasks, through: :tags