![](/img/trans.png)
[英]How do I Iterate over an array of objects to create new instances of a Class? Ruby on Rails
[英]In Ruby on Rails, how do I save many ActiveRecord objects that have references to other instances of the same class?
抽象地思考這個有點棘手,所以這里有一個例子:
模式文件
create_table "comments", force: :cascade do |t|
t.string "text"
t.bigint "post_id"
t.bigint "author_id"
t.bigint "parent_id"
t.index ["parent_id"], name: "index_comments_on_parent_id"
end
評論.rb
class Comment < ApplicationRecord
belongs_to :parent, class_name: 'Comment'
belongs_to :author, class_name: 'User'
belongs_to :post
end
評論控制器.rb
def update
# params contains data for a chain of comments, each the child of the preceding one
comment_data = params["_json"]
comments = []
comment_data.each do |cd|
com = Comment.new(text: cd["text"], post_id: cd["post_id"], author_id: cd["user_id"])
com.parent = comments.last
comments.push(com)
end
comments.each { |com| com.save }
end
然后我嘗試通過 HTTP post 請求發送一些東西:
> POST /comments HTTP/1.1
> Host: mywebsitebackend.net
> Content-Type: application/json
| [
| {
| "post_id" : 1,
| "user_id" : 99,
| "text" : "You suck!!!!!"
| },
| {
| "post_id" : 1,
| "user_id" : 1,
| "text" : "Be respectful, or I'll block you."
| },
| {
| "post_id" : 1,
| "user_id" : 99,
| "text" : "Typical *******, doesn't belive in free speech."
| }
| ]
我期望的是:將新評論保存到基礎數據庫中,並正確引用結構(即,第一條評論的父級為零,其他兩條評論則為前一條)。
我得到的是:第二條評論被保存到數據庫中,但沒有父 ID。 第三條評論已正確保存。 第一個完全丟失。
即使我手動檢查參考鏈並確保以正確的順序保存所有內容,我什至不知道如何獲得我需要的東西; 這個例子應該有效,因為首先保存了無父注釋。 我也不想深入研究:如果底層結構更復雜,每個評論有多個評論鏈和多個子級,它會變得非常復雜。 此外,Ruby和ActiveRecord不應該抽象出這樣的東西嗎?
那么我做錯了什么,當創建了同一個類的多個新 ActiveRecord 對象並且其中一些對象相互引用時,如何正確保存數據?
Rails 版本:5.1.7
操作系統:macOS 10.15.7 (Catalina)
數據庫:PostgreSQL 12.4
您所描述的稱為自引用關聯或自聯接。
要設置自引用關聯,您只需創建一個指向同一個表的可為空的外鍵列:
class CreateObservations < ActiveRecord::Migration[6.0]
def change
add_reference :comments, :parent,
null: true,
foreign_key: { to_table: :comments }
end
end
還有一個指向同一個類的關聯:
class Comment < ApplicationRecord
belongs_to :parent,
class_name: 'Comment',
optional: true,
inverse_of: :children
has_many :children,
class_name: 'Comment',
foreign_key: 'parent_id',
inverse_of: :parent
end
如果您不將列設置為可空,並且belongs_to
關聯是可選的,您最終會遇到雞與蛋的情況,在這種情況下您實際上無法在表中插入第一條記錄,因為它沒有任何可引用的內容。
如果要同時創建記錄和子項,請使用accepts_nested_attributes
。
class Comment < ApplicationRecord
belongs_to :parent,
class_name: 'Comment',
optional: true,
inverse_of: :children
has_many :children,
class_name: 'Comment',
foreign_key: 'parent_id',
inverse_of: :parent
accepts_nested_attributes_for :children
end
這使您可以使用以下內容創建 reddit 樣式的評論線程:
Comment.create!(
text: "You suck!!!!!",
user_id: 99,
children_attributes: [
{
text: "Be respectful, or I'll block you.",
user_id: 1,
children_attributes: [
{
text: "Typical *******, doesn't belive in free speech.",
user_id: 99
}
]
}
]
])
因為它會自動處理遞歸。 請參閱有關如何 為嵌套屬性創建表單以及如何在控制器中將它們列入白名單的指南。 如果嵌套屬性包含 id,則嵌套記錄將被更新而不是創建新記錄。
處理整個 post_id 問題可以通過不同的方式完成:
問題似乎是您只是在之后保存評論。 通常 Rails 只在記錄保存后生成 id,所以當你在保存之前分配 parent_id 時它仍然是 nil。 您可以在交易中添加所有這些,只是為了確定。 像這樣的東西:
def update
# params contains data for a chain of comments, each the child of the preceding one
comment_data = params["_json"]
comments = []
Comment.transaction do
comment_data.each do |cd|
com = Comment.new(text: cd["text"], post_id: cd["post_id"], author_id: cd["user_id"])
com.parent = comments.last if comments.present?
com.save!
comments.push(com)
end
end
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.