【问题标题】:Create or update object in table在表中创建或更新对象
【发布时间】:2016-07-14 10:06:12
【问题描述】:

如果它不存在,我想创建一个新对象;或者,如果它已经存在于我的数据库中,则只需更新它的电子邮件。 我从 CSV 文件导入数据。

我尝试使用find_or_create_by 方法,但当它已经存在时,仍然复制数据库中的对象。它不只是更新它。

人.rb

 class Person < ActiveRecord::Base
   validates :email,  uniqueness: true
   require 'csv'

   def self.import_data
     filename = File.join Rails.root, '/vendor/people.csv'

     CSV.foreach(filename, headers: true, col_sep: ',') do  |row|
       firstname, lastname, home_phone_number, mobile_phone_number,   email, address = row
       Person.find_or_create_by(firstname: row['firstname'], lastname: row['lastname'], home_phone_number: row['home_phone_number'], mobile_phone_number: row['mobile_phone_number'], email: row['email'], address: row['address'])
     end
   end
 end

schema.rb

   create_table "people", force: :cascade do |t|
t.string   "email"
t.string   "home_phone_number"
t.string   "mobile_phone_number"
t.string   "firstname"
t.string   "lastname"
t.string   "address"
t.datetime "created_at",          null: false
t.datetime "updated_at",          null: false
end

【问题讨论】:

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


    【解决方案1】:

    这是解决方案:

    person = Person.find_or_create_by(firstname: row["firstname"])
    person.update_attributes({firstname: row['firstname'], lastname: row['lastname'], home_phone_number: row['home_phone_number'], mobile_phone_number: row['mobile_phone_number'], address: row['address'], email: row['email']})
    

    我无法解释为什么这个解决方案有效,而不是您向我提议的那个。 但是感谢@sohail_khalil 和@abhilash 的帮助!

    【讨论】:

    • 虽然这个答案确实解决了手头的问题......这种方法最终肯定会失败......这个世界上有太多的“约翰史密斯”等...... @sohailkhalil 是正确的,因为查询应该基于唯一的列。如果可以更新电子邮件,则 CSV 中应该有一列"last_email" 或类似的东西......
    【解决方案2】:

    你需要这样做

    Person.find_or_create_by(email: row['email']) do |person|
      person.update_attributes(firstname: row['firstname'], lastname: row['lastname'], home_phone_number: row['home_phone_number'], mobile_phone_number: row['mobile_phone_number'], address: row['address'])
    end
    

    您做错了什么是将 find_or_create_by 应用于多个列。仅将其应用于一个。

    我希望这将帮助您并解决您的问题。

    【讨论】:

    • 我也试过你的方法,我得到了同样的错误。我在名字和姓氏上创建了find_or_create 方法,因为电子邮件是可以更改的属性。但是由于名字和姓氏已经存在,它不会进入循环来更新电子邮件。但是,当对象不存在并且要创建时,它可以正常工作。
    • 没有对名字和姓氏进行验证。您如何获得包含名字和姓氏的确切记录。找到一些其他独特的字段,您将从中获得准确的记录并更新电子邮件。现在唯一的电子邮件是唯一属性。
    • 抱歉,有一些我不明白的地方。电子邮件字段是唯一可以由用户更新的字段。因此,如果我对电子邮件执行find_or_create,它将找不到相应的对象,因为它是一封新电子邮件,因此它将创建一个新对象。如果我在名字上执行find_or_create,它不会更新它,因为名字已经存在并且它不会进入循环......我有点失落抱歉!
    • @Orsay 这种方法最终肯定会失败……这个世界上有太多的“约翰·史密斯”等。 @sohailkhalil 是正确的,因为查询应该基于唯一的列。如果可以更新电子邮件,CSV 中应该有一列"last_email" 或类似的东西...
    • 此解决方案不能解决所述问题!该块仅在创建的情况下执行。如果记录已经存在并被找到,则该块不被执行,也不会被更新!
    【解决方案3】:

    补充@sohail 所说的内容。为了使用多个参数来查找记录并随后更新或创建,您可以使用

    Person.where(<conditions>).first_or_initialize do |person|
       <initialize code>
    end
    

    应该可以的。

    【讨论】:

    • 我在firstnamelastname 上创建了where 方法,因为电子邮件是可以更改的属性。但是由于名字和姓氏已经存在,它不会进入循环来更新电子邮件。但是,当对象不存在并且要创建时,它可以正常工作。
    【解决方案4】:

    如果您有 Rails 3.2 或更高版本,请替换:

     Person.find_or_create_by(firstname: row['firstname'], lastname: row['lastname'], home_phone_number: row['home_phone_number'], mobile_phone_number: row['mobile_phone_number'], email: row['email'], address: row['address'])
    

    与:

    Person.where(:email => row['email']).first_or_create(:firstname => row['firstname'], :lastname => row['lastname'], :home_phone_number => row['home_phone_number'], :mobile_phone_number => row['mobile_phone_number'], :email => row['email'], :address => row['address'])
    

    【讨论】:

    • 这不会更新现有记录。如果记录已经存在,OP 想要更新属性。首先或创建使用OR - 所以如果它不存在,它将创建,但只返回已经存在的......
    猜你喜欢
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    相关资源
    最近更新 更多