rails中的相关关联

Bro*_*tse 5 ruby database-design ruby-on-rails

舞台:

假设我的模型定义如下:

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
Run Code Online (Sandbox Code Playgroud)

这背后的逻辑是:在页面上注册的每个客户端都能够注册多个不同的比赛,每个团队可以拥有多个驱动程序.然而,每个客户也是一个驱动程序,默认情况下,它将被分配给他的每个团队.为了达到这个目的,我已经覆盖了Team drivers阅读器(我正在使用andandgem):

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
Run Code Online (Sandbox Code Playgroud)

这很好用,但是并不像我希望的那样漂亮(每当我调用这个方法时它会进行额外的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
Run Code Online (Sandbox Code Playgroud)

这适用于大多数情况,但是由于client.driver_profile并且drivers完全不同的关联,添加到驱动程序中的错误drivers不会添加到client.driver_profiledriver_profile(第一页)的嵌套字段中,也不会显示.我已成功克服它:

(drivers + Array.wrap(client.andand.driver_profile)).uniq(&:object_id).each do |driver|
Run Code Online (Sandbox Code Playgroud)

然而,这非常hacky,丑陋且通常很糟糕,因此这是假设模型设计错误的完美时刻.

这个问题

您将如何为此重新设计数据库模型和关联?到目前为止,我想到了许多解决方案:

  1. 使用union来定义种族的驱动程序.如果只有union方法返回了可交换的AR关系,那么这绝对是最好的选择.
  2. 创建关联,other_drivers并定义方法drivers来对驱动程序和driver_profile求和.这样我就无法查询驱动程序结果.
  3. 'Hack'驱动程序关联的目标方法是向其添加driver_profile - 但是这不会在结果范围中持久存在.

mde*_*tis 0

我想知道,将您的驱动程序协会转换为多态协会怎么样?

我正在考虑这样的事情(注意:我使用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
Run Code Online (Sandbox Code Playgroud)

楷模:

# 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
Run Code Online (Sandbox Code Playgroud)

Race.first.racersmapon ,所以 on和实例上race_racers的错误应该没问题DriverCustomer