ruby-on-rails 在Rails活动记录中向上插入

mec1mxoz  于 2023-02-01  发布在  Ruby
关注(0)|答案(8)|浏览(202)

ActiveRecord有内置的upsert功能吗?我知道我可以自己写,但如果这样的东西已经存在,我显然不想写。

ubbxdtey

ubbxdtey1#

rails 6中有一个令人敬畏的新特性:他们把upsertupsert_all加到ActiveRecord
更多信息请访问https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-upsert_all

wlsrxk51

wlsrxk512#

Model.find_or_initialize可能会做你想要的事情。如果有意义的话,你可以用saveupdate_attributes链接它。
更多信息请参见导轨指南。

snz8szmq

snz8szmq3#

我刚路过这个图书馆:https://github.com/seamusabshere/upsert
我还没测试过,但看起来很有希望

yduiuuwa

yduiuuwa4#

Rails 6为https://github.com/rails/rails/pull/31989引入了create_or_find_by
对于大量记录,也可以使用https://github.com/zdennis/activerecord-import
示例:

Book.import [book], on_duplicate_key_update: [:title]
j2datikz

j2datikz5#

IMO Upsert机制需要为每个型号进行自定义配置。
因此,最好的解决方案是为模型实现自定义SQL查询,例如

insert into <table> (<field_1>, ..., <field_n>) 
  values (<field_1_value>, ..., <field_n_value>)
on duplicate key update
  field_x = field_x_value,
  ...
  field_z = field_z_value;
rjee0c15

rjee0c156#

还有模型.find_or_create

h9vpoimq

h9vpoimq7#

在Rails 6中,它有upsert方法,doc:

  1. https://apidock.com/rails/v6.0.0/ActiveRecord/Persistence/ClassMethods/upsert
  2. How does the upsert function in Rails work?
    用法:
Book table
---
id: integer
author_name: string
title: string

用法:(输入rails c

> Book.all   #=> []
> Book.upsert({ id: 1, author_name: 'Liu', title: 'Tripple body 1'})
> Book.upsert({ id: 1, author_name: 'Liu', title: 'Tripple body 1'})
> Book.upsert({ id: 1, author_name: 'Liu', title: 'Tripple body 1'})
> Book.all  # => only 1 book, with tile: 'Tripple body 1'

您可能会看到原始SQL如下所示:(在Postgres 14.2中)

INSERT INTO "books" ("author_name","title","created_at","updated_at") 
  VALUES ('Liu', 'Tripple body 1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) 
  ON CONFLICT ("id") 
  DO UPDATE SET updated_at=(
      CASE WHEN (
        "books"."author_name" IS NOT DISTINCT 
          FROM excluded."author_name" 
        AND "books"."title" IS NOT DISTINCT 
          FROM excluded."title"
      ) 
      THEN "books".updated_at ELSE CURRENT_TIMESTAMP END
    ),
    "author_name"=excluded."author_name",
    "title"=excluded."title" 
  RETURNING "id"

因此请注意:

  1. upsert将根据散列参数确定唯一性,因此请确保其中存在包含在表的唯一列中的键列(例如id列)
    1.如果在参数中不传递id或类似的唯一列,则它总是将新记录插入到表中
  2. upsert将跳过模型回调和验证。
    顺便说一句,我讨厌这种方法,我更喜欢find_or_create_by或只是:
unless Book.exists?("...")
  Book.create ...
end

这就更清楚了。

z9zf31ra

z9zf31ra8#

我曾写过一篇博客文章,探讨如何实现这一目标,请查看here
你必须写一个活动的记录扩展名,它看起来像这样。

module ActiveRecordExtension
  extend ActiveSupport::Concern

  def self.upsert(attributes)
    begin
        create(attributes)
    rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation => e
        find_by_primary_key(attributes['primary_key']).
        update(attributes)
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecordExtension)

相关问题