我试图在我的应用中执行find_or_create函数(类似于here),其中一个帖子有很多位置(通过locations_posts表)。不幸的是,代码似乎执行,找到正确的记录,然后继续创建一个新的记录,导致记录重复。知道为什么会发生这种情况吗?
Post.rb
class Post < ActiveRecord::Base
before_save :get_locations
# ===============
# = Before Save =
# ===============
def get_locations
self.locations = self.locations.collect do |location|
Location.find_or_create_by(name: location[:name])
end
end
# =================
# = Associations =
# =================
has_many :location_post
has_many :locations, :through => :location_post
accepts_nested_attributes_for :locations
posts_controller.rb
class PostsController < ::Blogit::ApplicationController
...
def create
@post = current_blogger.blog_posts.new(valid_params)
if @post.save
redirect_to @post, notice: t(:blog_post_was_successfully_created, scope: 'blogit.posts')
else
render action: "new"
end
end
日志
Parameters: {"utf8"=>"✓", "authenticity_token"=>"n4s...uAHATBRG1Nk=", "post"=>{"title"=>"Lorem ipsum dolor sit am
et", "body"=>"Consectetur adipiscing elit. Duis in diam in massa aliquet venenatis. Donec eleifend sed magna rutrum sagittis.", "locations_attributes"=>{"1402420506265"=>{"name"=>"London", "_destroy"=>"false"}}}, "commit"=>"Submit"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Unpermitted parameters: _destroy
(0.3ms) BEGIN
Location Load (0.6ms) SELECT "locations".* FROM "locations" WHERE "locations"."name" = 'London' LIMIT 1
SQL (0.5ms) INSERT INTO "blog_posts" ("blogger_id", "blogger_type", "body", "created_at", "title", "updated_at") VALUES ($1, $2, $3, $4, $
5, $6) RETURNING "id" [["blogger_id", 1], ["blogger_type", "User"], ["body", "Consectetur adipiscing elit. Duis in diam in massa aliquet ven
enatis. Donec eleifend sed magna rutrum sagittis."], ["created_at", "2014-06-10 17:15:09.564257"], ["title", "Lorem ipsum dolor sit amet"], [
"updated_at", "2014-06-10 17:15:09.564257"]]
SQL (0.4ms) INSERT INTO "locations" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", "2014-06-10 1
7:15:09.566659"], ["name", "London"], ["updated_at", "2014-06-10 17:15:09.566659"]]
SQL (0.4ms) INSERT INTO "location_posts" ("created_at", "location_id", "post_id", "updated_at") VALUES ($1, $2, $3, $4) [["created_at", "
2014-06-10 17:15:09.568250"], ["location_id", 376], ["post_id", 83], ["updated_at", "2014-06-10 17:15:09.568250"]]
SQL (0.3ms) INSERT INTO "location_posts" ("created_at", "location_id", "post_id", "updated_at") VALUES ($1, $2, $3, $4) [["created_at", "
2014-06-10 17:15:09.571852"], ["location_id", 1], ["post_id", 83], ["updated_at", "2014-06-10 17:15:09.571852"]]
(0.7ms) COMMIT
答案 0 :(得分:1)
哦,伙计,你的问题让我走了一个兔子洞。所以,让我回顾一下,您的前提很简单:在保存帖子之前,您需要确保如果数据库中已存在位置,则使用这些位置。听起来很简单,和它曾经用过。在rails 3.x中的东西
所以我重新创造了你的榜样,并且看到:我到处都是双打。
不知怎的,它始终记住添加的原始位置,然后它突然出现在我身上:它立即将新位置添加到location_posts
,然后当我更改locations
时,我实际上正在添加新位置location_posts
。
请放心:find_or_create_by
完美无缺:)
经过大量的反复试验后,我能够解决以下问题:
before_save :reuse_existing_locations
def reuse_existing_locations
new_locations = locations.map do |location |
if location.new_record?
new_loc = Location.find_by(name: location.name)
new_loc.present? ? new_loc : location.dup
else
location.dup
end
end
self.location_posts.reset
self.locations.replace( new_locations)
end
所以关键时刻:
dup
(复制)所有元素,否则当我执行替换或重置时,列表将为空location_posts
,使其为空, 之前插入&#34; new&#34;位置replace
用新的位置替换当前的位置数组(否则它也会添加)我有一种感觉这是一个rails-bug但不确定。至少它比以前困难得多。
答案 1 :(得分:0)
我的猜测是你可以删除before_save,只要它似乎不起作用:
self.locations是一种调用关系的关系,它收集活动记录数组中的所有现有项。如果这些项目不在表格中,只要它找不到任何不存在的内容,他就不会创建新项目。
嵌套参数是负责查找或创建该位置的参数。只要您定义从控制器传递给帖子模型的主键。
这是嵌套的工作流程:
如果嵌套参数不是自己创建位置(或更新,如果存在),则表示您没有传递正确的参数,或者您没有发送主键。