基于来自第三个HABTM模型的标准,来自两个模型的单个ActiveRecord集合

时间:2015-04-18 08:28:49

标签: ruby-on-rails ruby activerecord

我有一个由AddressesBusinessesIndividuals组成的地址簿。这三个模型都有HABTM关系,因为这就是现实的运作方式。

每个地址还有地理坐标(Address.latitudeAddress.longitude)。

我想在特殊点(/london)获取我的Rails控制器的集合,该集合在一个集合中具有所有业务在伦敦拥有地址的个人最终在传单地图上。

但我想要在伦敦以外的任何地址。

目前,

class Individual < ActiveRecord::Base
  has_and_belongs_to_many :addresses, join_table: :addresses_individuals
  has_and_belongs_to_many :businesses, join_table: :businesses_individuals

  scope :london, -> { joins(:addresses).where("addresses.latitude < ? AND addresses.latitude > ? AND addresses.longitude < ? AND addresses.longitude > ?", $London[:north], $London[:south], $London[:east], $London[:west]).distinct }

  def js_slug
    "#{self.first_name}_#{self.last_name}".parameterize.underscore.camelize(:lower)
  end
end

我使用@individuals = Individual.london在控制器中访问,它给了我所有拥有伦敦地址的个人,但如果有人也有伦敦以外的地址,那么它会被收集到列表中(如果我缩小,最终会在地图上显示。)

我为Address制作了类似的范围:

class Address < ActiveRecord::Base
  has_and_belongs_to_many :individuals, join_table: :addresses_individuals
  has_and_belongs_to_many :businesses, join_table: :businesses_addresses

  scope :london, -> { where("addresses.latitude < ? AND addresses.latitude > ? AND addresses.longitude < ? AND addresses.longitude > ?", $London[:north], $London[:south], $London[:east], $London[:west]) }
end

现在,

@individuals = Address.london.map{ |a| a.individuals }

接近我想要的,至少在控制台中,但当我把它放入控制器时:

class Frontend::FrontendController < ApplicationController

  layout :resolve_layout

  respond_to :html

  def london
    @individuals = Address.london.map{ |a| a.individuals }
    # @individuals = Individual.london
    @fitbounds = [[$London[:south], $London[:west]], [$London[:north], $London[:east]]]
    @circle_markers = nil
    render "city"
  end

观点:

<table class="table table-striped table-hover table-condensed table-responsive">
  <tbody>
    <% @individuals.each do |individual| %>
      <tr>
        <td id="<%= individual.js_slug %>">
          <%= link_to "#{individual.first_name} #{individual.last_name}", [:frontend, individual] %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

出错了,我得到了:

undefined method `js_slug' for Individual::ActiveRecord_Associations_CollectionProxy:Class

当我使用js_slug时,对@individuals = Individual.london的调用很正常,但之后我会在地图上获得伦敦不存在的所有地址。

因此,一旦通过@individuals = Individual.london收集并丢弃伦敦以外的地址,就不会遍历每个地址,我怎样才能获得这样一个有限的集合?

然后,当我对企业做同样的事情时,我如何将其与个人集合合并/加入?

谢谢!

2 个答案:

答案 0 :(得分:0)

问题是Address.london.map {| a | a.individuals}返回一个单独的元素,即个体集合(try @ persons.count,应该等于1)。

基本上我们在此之后是联接表中所有记录,其中address_id属于伦敦的地址,我们希望显示属于个人_id的个人。因此,我认为我们可以使用连接表中的范围获得您想要的内容,如下所示。

# address_individual model (sim for business)
class AddressIndividual < ActiveRecord::Base
  scope :london, -> { where("address_id in (?)", Address.london.map(&:id)) }
end

然后我们可以在控制器中调用它。我会使用与“个人”不同的名字。避免混淆(我们将在下面的视图中定义个人)。我只是使用&#39; address_individuals&#39;。

# individual controller
class Frontend::FrontendController < ApplicationController

  layout :resolve_layout
  respond_to :html

  def london
    @address_individuals = AddressIndividual.london

    @fitbounds = [[$London[:south], $London[:west]], [$London[:north], $London[:east]]]
    @circle_markers = nil
    render "city"
  end
end

然后我们可以在视图中引用这个,只需要一个额外的步骤来显示个人。

<table class="table table-striped table-hover table-condensed table-responsive">
  <tbody>
    <% @address_individuals.each do |ai| %>
      <% individual = Individual.find(ai.individual_id)
      <tr>
        <td id="<%= individual.js_slug %>">
          <%= link_to "#{individual.first_name} #{individual.last_name}", [:frontend, individual] %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

我认为这适合你。你可以为企业做同样的事情。我不确定如何将它们组合成单个范围或活动记录查询 - 我只会在视图中为每个范围执行两个循环。

答案 1 :(得分:0)

我将从底部开始。

当问题不存在时,它是最好的,对吗?您可能会问自己:不是寻找合并2个不同模型集合的方法:首先是否值得将它们分开?

我不是在谈论让他们成为一个模特,不是。我正在讨论单表继承,这将有效地允许您将这两个模型存储在一个表中(通过使这两个模型继承自拥有该表的模型,而不是AR::Base)。这意味着该单个表需要具有两个模型的字段和type字段。如果他们已经相似,那么你会没事的,如果没有,那么它是值得的&#34;是值得的#34;。这将允许您查询&#34;根模型&#34;只有当某些查询可能导致同时拥有个人和企业时:ActiveRecord才会实例化正确的对象。将基本模型中的常见因素分解为干旱。

现在是主要问题。

您似乎没有确定控制器中您想要的列表,在示例代码中您只提及个人,而您的投诉主要是您获得了不需要的地址。这是公平的,您没有明确地查询地址,而是根据另一个没有构造条件的数据集来获取它们。听起来比实际更复杂......

首先,离开&#34; londoning&#34;到地址模型并从其他地方引用它:

scope :london, -> { joins(:addresses).merge(Address.london).distinct }

其次,如果您需要地址列表,请查询地址:

@addresses = Address.london

如果您需要相关人员,each您遇到N + 1查询问题时,请按此方式绕过它:

@addresses = Address.london.includes(:individuals)

这与上面的代码完全相同。这不明显吗?好的,视图中的这个片段怎么样(为了简洁起见,使用Slim / HAML):

- @addresses.each do |address|
  - address.individuals.each do |individual|
    = individual.whatever

除了第二行之外绝对相同,因为您已经将地址映射到控制器中的集合(而不是个人本身!)。这也是@FunTimeFreddie所说的,他是对的。