使用Find_or_Create创建的重复模型

时间:2014-06-10 17:18:30

标签: ruby-on-rails ruby-on-rails-4

我试图在我的应用中执行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

2 个答案:

答案 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是一种调用关系的关系,它收集活动记录数组中的所有现有项。如果这些项目不在表格中,只要它找不到任何不存在的内容,他就不会创建新项目。

嵌套参数是负责查找或创建该位置的参数。只要您定义从控制器传递给帖子模型的主键。

这是嵌套的工作流程:

  • controller使用params创建一个新模型,并创建一个新实例 与嵌套参数相关的模型并开始一个事务。
  • 如果 保存第一个模型,将关系保存在现有ID上,或创建一个新模型。
  • 如果第一个模型或第二个模型失败,则事务回滚并且不保存任何内容。

如果嵌套参数不是自己创建位置(或更新,如果存在),则表示您没有传递正确的参数,或者您没有发送主键。

相关问题