在Rails中,如何将模型两次连接到多态属于的另一个模型?

时间:2014-09-04 20:07:07

标签: ruby-on-rails ruby ruby-on-rails-4 rails-activerecord polymorphic-associations

我试图确定在Rails 4中设置以下架构和关联的适当方法:

Business
  company_name
  ->1 street_address (Address)
  ->0..1 mailing_address (Address)
  ->0..n employees (Employee)

Employee
  first_name
  last_name
  birthday
  ->0..n addresses (Address)

Address
  street
  city
  state
  zip
  ->1 resource (Business or Employee or potentially something else in the future)

这里的主要目标是:

  1. 能够检索任意地址,然后找到它所属的资源(可能是企业或员工)
  2. 将一个地址指定为"街道地址"对于每个商家,以及可选的另一个地址作为"邮寄地址"
  3. 允许每个企业拥有多个员工,每个员工可能有其他不同的地址(家庭住址等)。
  4. 允许其他未来资源(尚未知)也有相关的地址
  5. 请注意,地址既可以属于业务(充当业务的街道地址或邮件地址),也可以属于员工(并且员工可以拥有多个地址)。这对我来说意味着多态关系,我在Rails中实现这一点的第一个尝试看起来像这样:

    class Business < ActiveRecord::Base
      has_one :street_address, class_name: 'Address', as: :resource, 
                               inverse_of: :resource, dependent: :destroy
      has_one :mailing_address, class_name: 'Address', as: :resource, 
                                inverse_of: :resource, dependent: :destroy
      has_many :business_employees, inverse_of: :business, dependent: :destroy
      has_many :employees, through: :business_employees
    end
    
    class BusinessEmpoloyee < ActiveRecord::Base
      belongs_to :business
      belongs_to :employee
    end
    
    class Employee < ActiveRecord::Base
      has_many :business_employees, inverse_of: :employee, dependent: :destroy
      has_many :businesses, through: :business_employees
      has_many :addresses, as: :resource, inverse_of: :resource, dependent: :destroy
    end
    
    class Address < ActiveRecord::Base
      belongs_to :resource, polymorphic: true
    end
    

    但是,这并不像希望的那样工作。业务的街道地址和邮寄地址始终返回相同的值。例如:

    2.1.2 :001 > b = Business.create(company_name: 'Test Business')
    
    2.1.2 :002 > b.street_address = Address.create(street: '123 Main St', city: 'San Francisco', state: 'CA')
    
    2.1.2 :003 > b.mailing_address = Address.create(street: 'P.O. Box 123', city: 'New York', state_code: 'NY')
    
    2.1.2 :004 > b2 = Business.find_by_company_name('Test Business')
      Business Load (1.2ms)  SELECT  "businesses".* FROM "businesses"  WHERE "businesses"."company_name" = 'Test Business' LIMIT 1
     => #<Business id: 1168, company_name: "Test Business", street_address_id: nil, mailing_address_id: nil, created_at: "2014-09-04 19:57:14", updated_at: "2014-09-04 19:57:14"> 
    
    2.1.2 :005 > b2.mailing_address
      Address Load (0.5ms)  SELECT  "addresses".* FROM "addresses"  WHERE "addresses"."resource_id" = $1 AND "addresses"."resource_type" = $2 LIMIT 1  [["resource_id", 1168], ["resource_type", "Business"]]
     => #<Address id: 1186, resource_id: 1168, resource_type: "Business", street: "P.O. Box 123", city: "New York", state: "NY", zip: nil, created_at: "2014-09-04 19:57:56", updated_at: "2014-09-04 19:57:56"> 
    
    2.1.2 :006 > b2.street_address
      Address Load (0.5ms)  SELECT  "addresses".* FROM "addresses"  WHERE "addresses"."resource_id" = $1 AND "addresses"."resource_type" = $2 LIMIT 1  [["resource_id", 1168], ["resource_type", "Business"]]
     => #<Address id: 1186, resource_id: 1168, resource_type: "Business", street: "P.O. Box 123", city: "New York", state: "NY", zip: nil, created_at: "2014-09-04 19:57:56", updated_at: "2014-09-04 19:57:56"> 
    

    当然,这确实有意义 - 业务部门如何知道哪个地址用于哪个地址值?

    那就是这样,关于我如何捕获这里描述的关系的任何建议?

1 个答案:

答案 0 :(得分:0)

经验法则:将关系写入belongs的模型中。如果为一个模型指定两个指向同一模型的关联,它们实际上将是相同的,因为只有一种方法可以归属:具有某个模型的resource_idresource_type

您需要为“所属”添加另一个参数,以便过滤“属于哪种属性”。一种相当标准的方法是单表继承(STI)(和I happened to write about how to use it),其工作方式如下:向type:string:index添加Address列并创建一个继承自的AddressMailingAddress,比方说,class MailingAddress < Address end

Address

大部分时间它应该是空的,它继承了type="MailingAddress"甚至存储的所有内容:它存储在同一个表中,但可以通过Address区分,当你查询时会自动添加MailingAddress来自数据库的这个类的实例。但是,当您查询{{1}} es时,根据继承原则,您还会得到{{1}} es。

执行相同操作的其他方法涉及类似的事情:添加另一列以过滤掉所有其他列的邮寄地址。字符串并不是最快速的比较,即使RDBMS在这方面的速度非常快。