【问题标题】:Deleting Record does not destroy association删除记录不会破坏关联
【发布时间】:2018-01-30 09:52:54
【问题描述】:

我有一个Task 模型和User 模型,我有一个名为Tag 的黑白连接表TaskUser。 任务.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

用户.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

标签.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 背景,所以以前从未经历过这种行为。 有没有办法我也可以删除关联(不使用任何 gem,有些人建议使用 paranoia gem 并用 real_destroy 替换 destroy!)

【问题讨论】:

标签: activerecord ruby-on-rails-5


【解决方案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

【讨论】:

  • 嘿@Kkulikovskis 感谢您的详尽解释,但我似乎不知道这将如何工作 has_many :tagged_tasks,通过: :tags,我是否还必须在 Tag 模型中进行更改跨度>
  • @Supertracer 不,您不需要更改标签模型。可能是您需要指定source: 属性的情况,因为tagged_tasksTag 关联的tasks 名称不匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-23
  • 1970-01-01
  • 2011-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多