在 Rails ActiveRecord 中更新插入

发布于 2024-10-12 06:12:01 字数 66 浏览 5 评论 0原文

ActiveRecord 是否有内置的更新插入功能?我知道我可以自己写它,但如果这样的东西已经存在,我显然不想这样做。

Does ActiveRecord have a built-in upsert functionality? I know I could write it myself but I obviously don't want to if such a thing already exists.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

哭了丶谁疼 2024-10-19 06:12:01

Rails 6 中有一个很棒的新功能:他们向 ActiveRecord 添加了 upsertupsert_all

可以在此处找到更多信息 https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html #method-i-upsert_all

There is an awesome new feature in rails 6: they added upsert and upsert_all to ActiveRecord

More info can be found here https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-upsert_all

瞎闹 2024-10-19 06:12:01

Model.find_or_initialize 可能会满足您的需求。如果有意义的话,您可以将其与 saveupdate_attributes 链接起来。

更多信息请参见 Rails 指南

Model.find_or_initialize likely does what you want. You can chain it with save or update_attributes if that makes sense.

More info in the Rails Guides.

时光是把杀猪刀 2024-10-19 06:12:01

我刚刚跑过这个图书馆:
https://github.com/seamusabshere/upsert

我还没有测试过,但看起来很有希望

I just ran across this library:
https://github.com/seamusabshere/upsert

I haven't tested it yet, but it looks promising

盗心人 2024-10-19 06:12:01

Rails 6 针对这种情况引入了 create_or_find_by https://github.com/rails/rails/pull/31989

此外,对于大量记录,可以使用 https://github.com/zdennis/ activerecord-import

示例:

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

Rails 6 introduces create_or_find_by for this case https://github.com/rails/rails/pull/31989

Also for bulk of records it is possible to use https://github.com/zdennis/activerecord-import

Example:

Book.import [book], on_duplicate_key_update: [:title]
高跟鞋的旋律 2024-10-19 06:12:01

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;

IMO Upsert mechanism requires custom configuration for each model.

So the best solution would be to implement a custom SQL query for a model, e.g.

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;
神妖 2024-10-19 06:12:01

还有Model.find_or_create

There is also Model.find_or_create

苍风燃霜 2024-10-19 06:12:01

从 Rails 6 开始,它有 upsert 方法,文档:

  1. https://apidock.com/rails/v6.0.0/ActiveRecord/Persistence/ClassMethods/upsert
  2. Rails 中的 upsert 函数如何工作?

用法:

Book table
---
id: integer
author_name: string
title: string

Usage : ( into 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 列)
  2. 如果您没有在参数中传递 id 或类似的唯一列,它总是将新记录插入表
  3. upsert 将跳过模型回调并且验证。

顺便说一句,我讨厌这种方法,我更喜欢 find_or_create_by 或只是:

unless Book.exists?("...")
  Book.create ...
end

这更清晰。

from Rails 6 , it has upsert method, doc:

  1. https://apidock.com/rails/v6.0.0/ActiveRecord/Persistence/ClassMethods/upsert
  2. How does the upsert function in Rails work?

usage:

Book table
---
id: integer
author_name: string
title: string

Usage : ( into 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'

and you may see the raw SQL looks like: ( in 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"

so be aware that:

  1. upsert will determine uniqueness from the hash parameter, so make sure there is the key column in it which included in the table's unique columns (such as id column )
  2. if you don't pass an id or similar unique column in the parameter, it always insert new record into table
  3. upsert will skip the model callbacks and validations.

btw, I hate this method, I prefer find_or_create_by or just:

unless Book.exists?("...")
  Book.create ...
end

which is clearer.

小傻瓜 2024-10-19 06:12:01

我写了一篇关于我们如何实现这一目标的博客文章。请查看此处

您必须编写一个活动记录扩展。它看起来像这样。

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)

I had written a blog post on how we can achieve this. Check it out here.

You'll have to write an active record extension. It will look something like this.

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文