如何在Rails中将属性映射到对象列表?

时间:2020-07-04 15:42:12

标签: ruby-on-rails ruby activemodel

我有四个型号。 EventInvitationProfileUser

它们具有以下关系:

  • Events有很多Invitations
  • Profiles有很多Invitations
  • Profiles属于Users
  • ...然后,Invitations显然属于EventsProfiles

我的问题是,当我用用户登录时,我可以访问其id的前端,并且基于此,我想查询Events的列表这样我就可以知道某个用户(他们的个人资料)是否已经被邀请,或者他们仍然可以请求邀请。

我想要的伪描述如下:接受所有事件,如果一个事件具有邀请,请检查其中一个是否属于属于我的用户的个人资料。如果是这样,则向该特定事件添加一个Applied = true字段。返回所有事件,无论应用的是true还是false

我猜它是由wherejoin组成的一条很好的链接,但是我想我在这里用我的Ruby知识碰壁了。

谢谢您的指导!

2 个答案:

答案 0 :(得分:3)

我将写出代码,使设置清晰明了。

class Invitation < ApplicationRecord
  belongs_to :event
  belongs_to :profile
end

class Event < ApplicationRecord
  has_many :invitations
end

class Profile < ApplicationRecord
  has_many :invitations
  belongs_to :user
end

class User < ApplicationRecord
  has_many :profiles
end

如果您想了解某个事件的用户,请使用has_many :through通过另一个用户访问关联。首先,我们将个人资料和活动设置为通过邀请相互交流。

class Event < ApplicationRecord
  has_many :invitations
  has_many :profiles, through: :invitations
end

class Profile < ApplicationRecord
  belongs_to :user
  has_many :invitations
  has_many :events, through: :invitations
end

现在我们可以将用户设置为通过个人资料获得邀请,并通过邀请获得活动。

class User < ApplicationRecord
  has_many :profiles
  has_many :invitations, through: :profiles
  has_many :events, through: :invitations
end

我们对Event进行同样的操作,以通过个人资料找到其用户。

class Event < ApplicationRecord
  has_many :invitations
  has_many :profiles, through: :invitations
  has_many :users, through: :profiles
end

现在,事件可以找到他们的用户,而用户可以找到他们的事件。

users = event.users
events = users.events

Rails现在知道如何将事件与用户连接起来,从而使您的任务以及许多其他任务变得更加容易。

您要避免的是必须将每个事件加载到内存中。您还希望避免查询事件列表,然后查询每个事件是否与您的用户匹配的N + 1查询。那会变得很慢。我们希望获取所有事件,并检查是否已在单个查询中应用它们。

首先,我们将伪列属性添加到名为Applied的Event上。这样我们就可以存储查询中的一些额外信息。

class Event < ApplicationRecord
  has_many :invitations
  has_many :profiles, through: :invitations
  has_many :users, through: :profiles

  attribute :applied, :boolean
end

然后编写一个填充它的查询。我们可以利用刚刚建立的Event-> User关系来直接与User加入Event。

# We can't use bind parameters in select, quoting will have to do.
user_id = ActiveRecord::Base.connection.quote(user_id)

@events = Event
  # A left outer join ensures we get all events, even ones without users.
  .left_outer_joins(:users)
  .select(
    # Grab all of event's columns plus...
    "events.*",

    # Populate applied.
    "(users.id = #{user.id}) as applied"
  )

现在,您可以在单个查询中有效地遍历事件,而不必将所有事件都加载到内存中。

@events.find_each { |event|
  do_something if event.applied
}

最后一点是将其打包为scope,以便于使用。

class Event < ApplicationRecord
  has_many :invitations
  has_many :profiles, through: :invitations
  has_many :users, through: :profiles

  attribute :applied, :boolean

  scope :applied_to_user, ->(user_id) {
    # We can't use bind parameters in select, quoting will have to do.
    user_id = ActiveRecord::Base.connection.quote(user_id)

    left_outer_joins(:users)
      .select(
        # Grab all of event's columns plus...
        "events.*",
    
        # Populate applied.
        "(users.id = #{user.id}) as applied"
      )
  }
end

Event.applied_to_user(user_id).find_each { |event|
  ...
}

答案 1 :(得分:1)

您想知道某个事件是否有邀请,其中任何邀请都属于给定用户的个人资料,因此请在Event上定义一个方法,以准确告知您:

# app/models/event.rb
class Event < ApplicationRecord
  def applied(user_id)
    invitations.any? do |invitation|
      invitation.profile.user_id == user_id
    end
  end
end

# app/controllers/events_controller.rb
class EventsController < ApplicationController
  def index
    @events = Event.all
    @current_user = current_user
  end
end

然后我想您想在类似视图的列表中显示该列表:

# app/views/events/index.erb
<%= render @events %>

# app/views/events/_event.erb
<tr>
  <td> <%= event.name %> </td>
  <td> <%= event.applied(@current_user.id) %>
</tr>

现在有很多方法可以提高效率,例如渴望加载,但这是建议的基本方法。这符合您的需求吗?

相关问题