我有四个型号。 Event
,Invitation
,Profile
和User
。
它们具有以下关系:
Events
有很多Invitations
Profiles
有很多Invitations
Profiles
属于Users
Invitations
显然属于Events
和Profiles
我的问题是,当我用用户登录时,我可以访问其id
的前端,并且基于此,我想查询Events
的列表这样我就可以知道某个用户(他们的个人资料)是否已经被邀请,或者他们仍然可以请求邀请。
我想要的伪描述如下:接受所有事件,如果一个事件具有邀请,请检查其中一个是否属于属于我的用户的个人资料。如果是这样,则向该特定事件添加一个Applied = true字段。返回所有事件,无论应用的是true还是false
我猜它是由where
和join
组成的一条很好的链接,但是我想我在这里用我的Ruby知识碰壁了。
谢谢您的指导!
答案 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>
现在有很多方法可以提高效率,例如渴望加载,但这是建议的基本方法。这符合您的需求吗?