回调 - 在模型上设置字段

时间:2015-06-07 09:53:01

标签: ruby-on-rails callback activemodel

我正在尝试以下方法 - 我有模特:故事,书籍,关键词

class Tale < ActiveRecord::Base
has_many :tale_culture_joins
has_many :cultures, through: :tale_culture_joins
has_many :tale_purpose_joins
has_many :purposes, through: :tale_purpose_joins
has_many :tale_book_joins
has_many :books, through: :tale_book_joins
has_many :tale_keyword_joins
has_many :keywords, through: :tale_keyword_joins

class Book < ActiveRecord::Base
has_many :tale_book_joins
has_many :tales, through: :tale_book_joins
end

class TaleBookJoin < ActiveRecord::Base
belongs_to :tale
belongs_to :book
end

class Keyword < ActiveRecord::Base
has_many :tale_keyword_joins
has_many :tales, through: :tale_keyword_joins
end

class TaleKeywordJoin < ActiveRecord::Base
belongs_to :tale
belongs_to :keyword
end

这些是迁移

class CreateTales < ActiveRecord::Migration
  def change
    create_table :tales do |t|
    t.text :name, null: false, unique: true
    t.boolean :exists, default: nil
    t.timestamps null: false
    end
  end
end

class CreateBooks < ActiveRecord::Migration
  def change
  create_table :books do |t|
    t.text :name, null: false, unique: true
    t.boolean :exists, default: nil
    t.timestamps null: false
  end
end
end

class CreateKeywords < ActiveRecord::Migration
  def change
    create_table :keywords do |t|
      t.text :name, null: false, unique: true
      t.boolean :exists, default: nil
      t.timestamps null: false
    end
  end
end

我想要发生的是每次我通过以下方法删除(Tale,Book)或(Tale,Keyword)之间的连接 tale_instance_object.book_ids = []

它应该检查关系被破坏的书籍是否有任何其他故事关系。如果没有,则设置:Book对象实例中存在为false。

我可以通过控制器代码完成此操作。 想知道如何使用CallBacks或ActiveModel

1 个答案:

答案 0 :(得分:1)

只有当关系本身就是一个对象时,才应该真正使用连接类。 考虑以下情况:

doctors -> appointments <- patients
years -> days <- hours

在这些情况下,关系对象具有自己的数据(appointments.timedays.weekday)和逻辑。否则你只是浪费内存,因为对象必须为每个关系实例化。请改用has_and_belongs_to

class Tale < ActiveRecord::Base
  # has_and_belongs_to_many :cultures
  # has_and_belongs_to_many :purposes
  has_and_belongs_to_many :books
  has_and_belongs_to_many :keywords 
  after_destroy :update_books!
end

class Book
  has_and_belongs_to_many :tales

  def check_status!
    self.update_attribute(status: :inactive) unless books.any
  end
end

class Keyword
  has_and_belongs_to_many :tales
end 

同样exists对于模型字段来说是一个非常糟糕的命名选择,因为它与exists?方法中构建的Rails发生冲突。这将产生意想不到的后果。

更好的选择是使用整数with an enum

class Book
  # ...
  enum :status [:inactive, :active] # defaults to inactive
end

class CreateBooks < ActiveRecord::Migration
  def change
  create_table :books do |t|
    t.text :name, null: false, unique: true
    t.integer :status, default: 0
    t.timestamps null: false
  end
end

这也会添加方法book.inactive?book.active?

你可以向Tale添加一个回调,告诉Book更新故事时更新但是这段代码真的很臭,因为Tale现在负责维护Book的状态。

class Tale < ActiveRecord::Base
  # ...
  after_destroy :update_books!

  def update_books!
    self.books.each { |b| b.check_status! }
  end
end

class Book
  has_and_belongs_to_many :tales

  def check_status!
    self.update_attribute(status: :inactive) unless tales.any?
  end
end

更好的替代方法是在书籍上添加回调或在销毁故事时在控制器中执行回调:

class Book
  has_and_belongs_to_many :tales

  before_save :check_for_tales!, if: -> { self.tales_changed? }

  def check_for_tales!
    self.status = :inactive unless self.tales.any?
  end
end