简体   繁体   English

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

[英]In Rails, how to connect a model twice to another model that it polymorphically belongs_to?

I'm trying to determine the appropriate way to setup the following schema and associations in Rails 4: 我正在尝试确定在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)

The main goals here are: 这里的主要目标是:

  1. to be able to retrieve an arbitrary Address and in turn find the resource that it belongs to (which may be a Business or an Employee) 能够检索任意地址并依次找到其所属的资源(可能是企业或雇员)
  2. to have one Address designated as the "street address" for each Business, and optionally another address as the "mailing address" 将每个公司的一个地址指定为“街道地址”,还可以将另一个地址指定为“邮寄地址”
  3. to allow each Business to have multiple Employees, each of which may have other different addresses (home address, etc). 允许每个企业拥有多个员工,每个员工可能具有其他不同的地址(家庭住址等)。
  4. to allow other future resources (as-yet-unknown) to also have associated Addresses 允许其他未来资源(迄今未知)也具有关联的地址

Note that an Address can either belong to a Business (acting as either the street address or the mailing address for the business), or to an Employee (and an employee can have multiple addresses). 请注意,一个地址既可以属于一个公司(充当该公司的街道地址或邮寄地址),也可以属于一个雇员(一个雇员可以有多个地址)。 This to me implies a polymorphic relationship, and my first stab at implementing this in Rails looks like this: 对我而言,这意味着多态关系,而我在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

However, this isn't working as hoped. 但是,这没有达到预期的效果。 The street address and mailing address for the business are always both returning the same value. 商家的街道地址和邮寄地址总是返回相同的值。 For example: 例如:

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"> 

Of course, this does make sense - how is the Business suppose to know which Address to use for which address value? 当然,这确实是有道理的-业务部门应该如何知道将哪个地址用于哪个地址值?

So, that being the case, any suggestions on how I can capture the relationships described here? 那么,既然如此,我如何捕捉到这里所描述的关系有什么建议?

A rule of thumb: relationship is written into a model that belongs . 经验法则:关系被写入belongs的模型中。 If you specify for one model two associations pointing to the same model they will effectively be the same, as there is only one way to belong: have a certain model's resource_id and resource_type . 如果为一个模型指定两个指向同一模型的关联,则它们实际上将是相同的,因为只有一种归属方式:具有某个模型的resource_idresource_type

You'll need to add another parameter to "belonging" in order to filter "what kind of belonging that is". 您需要在“所属”中添加另一个参数,以便过滤“属于哪种”。 A rather standard way for that is single table inheritance (STI) (and I happened to write about how to use it ), which works as follows: you add a type:string:index column to Address and create a new class that inherits from Address , say, MailingAddress : 一种相对标准的方法是单表继承(STI)( 我碰巧写过有关如何使用它的方法 ),其工作方式如下:您向Address添加一个type:string:index列,并创建一个继承自它的新类Address ,例如MailingAddress

class MailingAddress < Address
end

Most of the time it should be empty, it inherits everything from Address , even storage: it's stored in the same table, but can be distinguished by type="MailingAddress" which is added automatically when you query for instances of this class from the databse. 大多数情况下,它应该为空,它继承自Address甚至存储的所有内容:存储在同一张表中,但可以通过type="MailingAddress"进行区分,当您从数据库查询此类的实例时,该类型将自动添加。 However, when you query for Address es, you also get MailingAddress es, per inheritance principle. 但是,当您查询Address es时,根据继承原理,您还将获得MailingAddress es。

Other ways of doing the same involve something similar: adding another column to filter out mailing addresses from all the others. 执行此操作的其他方法涉及相似的内容:添加另一列以从所有其他地址中过滤出邮件地址。 A string is not the fastest thing to compare, even though RDBMS are insanely fast at doing that. 即使RDBMS如此之快,字符串也不是最快的比较对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM