舞台:
假设我的模型定义如下:
class Client < AR:Base
has_one :driver_profile, class_name: 'Driver'
has_many :races
end
class Driver
# attribute :dob
belongs_to :client
has_and_belongs_to_many :teams
end
class Race
# attribute :date
belongs_to :client
has_and_belongs_to_many :drivers
end
这样做的逻辑是:页面上注册的每个客户都可以注册许多不同的种族,每个团队可以有多个驱动程序。但是,每个客户也都是驱动程序,默认情况下,将其分配给他的每个团队。为了实现这一点,我重写了Team
drivers
阅读器(我正在使用andand
gem):def drivers
association = super
driver_profile = client.andand.driver_profile
if !driver_profile || driver_profile.new_record? || association.include? driver_profile
association << client.driver_profile
end
association
end
这工作得很好,但是却不如我希望的那样好(并且每次我调用此方法时,都会进行一次额外的db调用)。
问题
对于所有模型,我都有一个非常庞大的多步骤表单。在前两个步骤中,用户可以编辑他的个人信息(在客户模型上),driver_profile和他的初始比赛。在随后的步骤中,他可以添加任意数量的车手参加比赛,但是,车手的出生日期需要与比赛日期进行核对(必须至少为21)。
问题在于,此验证不会落在驾驶员模型上,因为驾驶员可能对一场比赛有效,而对第二场比赛无效。因此,这是种族问题,而不是车手问题。我写了这样的验证(在Race模型上):
validate :drivers_at_least_21
def drivers_at_least_21
error = false
drivers.each do |driver|
if driver.age_at(start) < 21
driver.errors.add(:dob, :too_young)
error = true
end
end
errors.add(:driver, :invalid) if error
end
这在大多数情况下都有效,但是由于
client.driver_profile
和drivers
是完全不同的关联,因此添加到drivers
中的驱动程序的错误不会添加到client.driver_profile
中,也不会显示在driver_profile的嵌套字段中(第一页)。我设法使用以下方法克服了它:(drivers + Array.wrap(client.andand.driver_profile)).uniq(&:object_id).each do |driver|
但是,此操作非常笨拙,丑陋且通常很糟糕,因此这是假设模型设计错误的理想时机。
问题
您将如何为此重新设计数据库模型和关联?到目前为止,我已经想到了许多解决方案:
other_drivers
的关联,并定义方法drivers
以对驱动程序和driver_profile求和。这样,我将无法查询驱动程序结果。 最佳答案
我想知道,如何将您的驱动程序关联转换为多态关联?
我正在考虑这样的事情(注意:我使用 racers
以避免与 Driver
实例混淆):
移民:
# RaceRacer migration
class CreateRaceRacers < ActiveRecord::Migration
def change
create_table :race_racers do |t|
t.references :race, index: true
t.integer :racer_id
t.string :racer_type
t.timestamps
end
end
end
楷模:
# RaceRacer model
class RaceRacer < ActiveRecord::Base
belongs_to :race
belongs_to :racer, polymorphic: true
end
# Race model
class Race < ActiveRecord::Base
has_many :race_racers
has_many :driver_racers, through: :race_racers, source: :racer,
source_type: :Driver
has_many :customer_racers, through: :race_racers, source: :racer,
source_type: :Customer
# Unfortunately ActiveRecord doesn't support has_many on a polymorphic
# association without :source_type, so we need to define a method
def racers
race_racers.map(&:racer)
end
end
# Driver model
class Driver < ActiveRecord::Base
has_many :race_racers, as: :racer
has_many :races, through: :race_racers
end
# Customer model
class Customer < ActiveRecord::Base
has_many :race_racers, as: :racer
has_many :races, through: :race_racers
end
Race.first.racers
是 map
上的 race_racers
,所以 Driver
和 Customer
实例上的错误应该没问题关于ruby-on-rails - Rails中的相关协会,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22783645/