我已经建立了三个模型:User,List和UserList,后者是具有has_many_through关系的User和List之间的联接模型。
UserList的“加入模型”也有一个名为“评级”的列。
我想检索给定用户的所有“列表”记录,以及用户列表中每个列表记录的等级。但是,我做错了事,已经转了一些圈。希望对我不了解的内容提供指导。
这是三个模型:
class User < ApplicationRecord
has_many :user_lists
has_many :lists, through: :user_lists, dependent: :destroy
End
class List < ApplicationRecord
has_many :user_lists
has_many :users, through: :user_lists, dependent: :destroy
# no duplicate titles in the List table
validates :title, uniqueness: true
End
class UserList < ApplicationRecord
belongs_to :list
belongs_to :user
# a given user can only have one copy of a list item
validates :list_id, uniqueness: { scope: :user_id }
end
检索属于当前用户的所有(且仅)列表项,以及UserList模型中每个列表项的等级。
# list_controller.rb
def index
@lists = current_user
.lists
.joins(:user_lists)
.select('lists.*, user_lists.id as user_lists_id, user_lists.rating as rating')
.order(:id)
render json: @lists
end
我从各种Stack Overflow文章中共同总结了上述内容。但这不是很正确,我不确定为什么。
这是运行控制器索引代码时的rails控制台输出:
List Load (1.2ms)
SELECT DISTINCT
lists.*,
user_lists.id as user_lists_id,
user_lists.rating as rating
FROM "lists"
INNER JOIN "user_lists" "user_lists_lists"
ON "user_lists_lists"."list_id" = "lists"."id"
INNER JOIN "user_lists"
ON "lists"."id" = "user_lists"."list_id"
WHERE "user_lists"."user_id" = $1
ORDER BY "lists"."id" ASC [["user_id", 1]]
[#<List:0x00007fef741da338
id: 2,
title: "useronly",
created_at: Sat, 09 Feb 2019 09:22:44 UTC +00:00,
updated_at: Sat, 09 Feb 2019 09:22:44 UTC +00:00>,
#<List:0x00007fef741d9f78
id: 3,
title: "The Art of Gathering",
created_at: Sat, 09 Feb 2019 09:23:05 UTC +00:00,
updated_at: Sat, 09 Feb 2019 09:23:05 UTC +00:00>]
正在正确检索列表项-即ID,标题,created_at和updated_at。并且,仅适用于current_user(即,不是其他用户的项目)。 但是,user_lists中的评级列未正确检索。
rails控制台输出的这一部分看起来根本不正确:
INNER JOIN "user_lists" "user_lists_lists"
ON "user_lists_lists"."list_id" = "lists"."id"
但是坦率地说,我不确定如何解决它。我已经尝试过基于Googling + Stack-Overflowing的各种方法,但是没有任何效果。
有什么想法吗?非常感谢。
基于Obermillerk的delegate
指南,我认为为了将List模型中的“ title”添加到每个“ user_list”项目,我需要执行以下操作:
# lists_controller.rb (lines 9 + 11 indicated below)
def index
lists = current_user.user_lists
9: lists.each do |list|
# retrieve title to each record in user_list. Title is delegated to List
11: list.title = list.title
end
render json: lists
end
但是,这还不太有效-视图为空;服务器控制台显示:
00:01:22 api.1 | UserList Load (2.2ms) SELECT "user_lists".* FROM "user_lists" WHERE "user_lists"."user_id" = $1 [["user_id", 1]]
00:01:22 api.1 | ↳ app/controllers/api/v1/lists_controller.rb:9
00:01:22 api.1 | User Update (1.5ms) UPDATE "users" SET "last_login" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["last_login", "2019-02-12 08:01:22.664620"], ["updated_at", "2019-02-12 08:01:22.676667"], ["id", 1]]
00:01:22 api.1 | ↳ app/controllers/api/v1/users_controller.rb:44
00:01:22 api.1 | List Load (0.5ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
00:01:22 api.1 | ↳ app/models/user_list.rb:6
00:01:22 api.1 | Completed 500 Internal Server Error in 55ms (ActiveRecord: 11.1ms)
00:01:22 api.1 |
00:01:22 api.1 |
00:01:22 api.1 |
00:01:22 api.1 | NoMethodError (undefined method `title=' for #<UserList:0x00007f9220002b48>
00:01:22 api.1 | Did you mean? title):
00:01:22 api.1 |
00:01:22 api.1 | app/controllers/api/v1/lists_controller.rb:11:in `block in index'
00:01:22 api.1 | app/controllers/api/v1/lists_controller.rb:9:in `index'
答案 0 :(得分:0)
我检查了您的脚本,一切正常。我没有找到您在问题中提到的问题。当我运行此索引操作时,结果如下。您可以在末尾看到评分。
[{"id":3,"title":"This is list","created_at":"2019-02-09T16:19:18.552Z","updated_at":"2019-02-09T16:19:18.552Z","user_lists_id":2,"rating":"5"}]
我认为您没有提供足够的数据。您可以尝试放入以下数据吗
u = User.create(:name => "another", :email => "ok@gmail.com")
l = List.create("This is list") // creating first
UserList.create(:user_id => 1, :list_id => 3,:rating => 5)
请确保您检查ID,并将正确的ID放在UserList中。请注意,最佳实践推荐的ListUser名称应为连接表的字母顺序。
答案 1 :(得分:0)
让我看看是否能再次帮助您,我现在觉得很投入;)
我相信您在这里的问题是,您误会了活跃记录领域。对于活动记录查询,最终将检索特定模型结构的数据,在这种情况下,该数据就是列表结构。 List结构永远不能包含来自另一个模型的数据字段,只能包含它自己的数据字段。
因此,联接的目的不是在模型中添加数据字段,而是使您可以基于其他表中关联记录的数据来优化结果。也就是说,如果您只想包含来自特定用户的评分大于3的列表项,则可以使用join来包含UserList表,然后使用WHERE子句将您的结果过滤为仅包含评分大于3的那些大于3。但是,所得的List对象将仅包含其自身的数据,而UserList数据中则不包含任何数据。我不相信有任何方法可以注入一个字段。
我认为您实际上应该做的是使用UserList模型而不是List模型。 UserList模型具有您要查找的等级,您始终可以直接从UserList访问列表,因为它具有直接的一对一关联。从列表到用户列表需要您正在寻找用户列表的用户的额外输入,这会使事情变得复杂。
在活动记录中可以做的一件很酷的事情,可能就是您正在寻找的是对关系的委托方法处理。也就是说,您可以告诉UserLists,而不是自己调用title而不提出任何内容,而是将其发送到正确的List对象并返回结果。这样,您可以在UserList对象中委派List项目的所有数据字段,例如title
和您拥有的内容,然后UserList对象基本上可以充当其List对象的包装器。为此,您可以在模型顶部附近添加这样的一条线,最好将其放置在关联之后:
class UserList < ApplicationRecord
belongs_to :list
belongs_to :user
# delegate the :title method to the :list relation above
delegate :title, to: :list
# a given user can only have one copy of a list item
validates :list_id, uniqueness: { scope: :user_id }
end
您可以在一个呼叫中添加任意多个要委派的字段,所以说还有year
和director
字段,您可以执行delegate :title, :year, :director, to: :list
如果您确实希望将UserList变成List对象的包装器,则可能会更好,那就是说delegate_missing_to :list
,然后在UserList上未定义的任何方法都会自动尝试通过清单。这样,您可以将新方法和字段添加到List模型中,并将它们自动委派到UserList中。
希望这会有所帮助,很高兴根据需要澄清任何内容!