質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

2回答

1124閲覧

csvのデータから取り込んで、dbを更新または新規作成したい。

oiykram

総合スコア11

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2020/08/29 01:20

編集2020/08/29 06:14

■ CSVのユーザー情報とdbのユーザー情報を突合し、変更点がある場合は更新する
■ CSVに存在しないユーザーは新規に登録する
をしたいと思ってます。
@csv_users = User.create_list_from_csv(params[:file])
で、ファイルからデータを取り出して@csv_usersに代入しました。
その後に↓↓
①@csv_usersにちゃんとデータが入っているかどうかの確認をしたい(validate)
②Userモデルに該当するユーザーが、@csv_usersにいた場合は、更新(update)する
③Userモデルに該当するユーザーが、@csv_usersにいなかった場合は、新規作成(create)する

としたいのですが、

単体だと、User.where(id: csv_user.id ) == nil で条件分岐できると思うのですが
複数人の場合だとどうすればいいのでしょうか??
eachなど使用して、繰り返し処理を行うといいのでしょうか?

ご教授お願いします????????

********************************
csvからデータをインポートした場合、各カラムの情報はparametarではないので、
validate(params[:id])といった形にするのは難しいですよね?
*すでに createアクション、updateアクションで、model/user.rbにvalidateのメソッドを作成していたため、同じものを使いたかったため)

controller

1 def import 2 @csv_users = User.create_list_from_csv(params[:file]) 3 4 @csv_users.each do |csv_users| 5 import_user = User.create!( 6 id: values["id"], 7 name: csv_users.name, 8 age: csv_users.age, 9 ) 10 end 11 redirect_to users_url 12 end

model

1 class << self 2 def create_list_from_csv(file) 3 csv_articles = [] 4 CSV.foreach(file.path, headers: true) do |row| 5 values = row.to_h 6 csv_articles << User.new( 7 id: values["id"], 8 name: values["name"], 9 age: values["age"], 10 ) 11 end 12 csv_articles 13 end

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

no1knows

2020/08/29 02:44

> ■ CSVのユーザー情報とdbのユーザー情報を突合し、変更点がある場合は更新する > ■ CSVに存在しないユーザーは新規に登録する この要件からだけだと、一見すると、RailsからユーザーのCSVをエクスポートすれば良いように見えますが・・・ DB側もCSV側も双方でユーザーレコードの追加・更新があるということなのでしょうか?
oiykram

2020/08/29 04:51

no1knowsさん 返信ありがとうございます。いや、更新や新規追加があった場合にCSV側でデータを送ってDBと照合して変更する形です。。
guest

回答2

0

おっしゃる通り、eachで良さそうに思えます。

eachの中身としては、find_or_initialize_byおよびupdateの組み合わせで要件を満たしそうです
(以下参考記事です。updateupdate_attributesは同じです)
https://www.te-nu.com/entry/2017/02/16/233416

また、例外を発生させたいならupdate!にすれば大丈夫かと思います。
ここらへんを詳しく知りたい場合は以下参考記事が役立つかもしれません。
https://qiita.com/tyamagu2/items/8abd93bb7ab0424cf084

投稿2020/08/29 02:45

kumackey

総合スコア59

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oiykram

2020/08/29 04:59

kumackeyさん ありがとうございます。参考にして検討してみます!
guest

0

ベストアンサー

validateした結果、NGになったものをどうするのか、によって作戦は変わりますが、いずれにせよ一件づつvalidateするしかないです。
例えば

users = csv_articles.inject({true =>[],false => []]){|user,res| res[user.valid?] << user }

として、users[true]にvalidなもの、users[false]にinvalidなものを集めて処理するということになります。
validのものを処理するには、createとupdateが混在する前提ですと
rails6ですとまとめて処理出来る手段が用意されたそうです。まだ試してないです。
rails5ですとないので一件づつありなしを find_or_initialize_by で試しながら、ということになります。
まとめてinsert/updateしてくれるgemがありますが、これdatabaseエンジンによって update が動かないので、ありなし分けて行う必要がでてきます。

csv_articles find_or_initalize

投稿2020/08/29 02:25

winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oiykram

2020/08/29 05:03

winterboumさん ありがとうございます。 validateしてダメだった場合には、その箇所の指摘をしてもう一度インポートする形にしようと思います。参考にしてやってみます!
oiykram

2020/08/29 13:40

winterboumさん users = csv_articles.inject({true =>[],false => []]){|user,res| res[user.valid?] << user } は、例えば、 validates :name, uniqueness: true validates :age, presence: true を設定しているとして、 validatesがtureなのもは、users[true]に格納され、 validatesがfalseなのもは、users[false]に格納され、 その後、それぞれに処理するという認識であってますか??
oiykram

2020/08/29 13:58

ありがとうございます。
oiykram

2020/08/29 14:34

NoMethodError in UsersController#import undefined method `valid?' for {true=>[], false=>[]}:Hash Did you mean? value? Extracted source (around line #26): でエラーが出てしまいましたね????????
winterboum

2020/08/29 23:09

ん? {true=>[], false=>[]} に valid? code を写し間違えてるね、きっと
oiykram

2020/08/30 00:13

def create_list_from_csv(file) csv_articles = [] CSV.foreach(file.path, headers: true) do |row| values = row.to_h csv_articles << User.new( id: values["id"], name: values["name"], age: values["age"], ) end users = csv_articles.inject({true =>[],false => []}){ |user,res| res[user.valid?] << user } end ではないですか?????
winterboum

2020/08/30 02:40

あ! ごめん間違えたのは私。 { | red, user| res[user.valid?] << user } です。 each_with_object の話を寸前までしてて、それが残ってた
oiykram

2020/08/30 02:58

結果と要素が逆だったんですね。私も気づかず申し訳ありません。 親切に解説していただきありがとうございます。 TypeError in UsersController#import no implicit conversion of true into Integer とエラーが出てしまいました。なんとか解決してみようと思います。
winterboum

2020/08/30 04:26

typo | red, user ⇒ | res, user
oiykram

2020/08/30 05:01

ありがとうございます。そちらは修正できました!
oiykram

2020/08/30 05:36

TypeError in UsersController#import no implicit conversion of true into Integer のエラーは、user.vaild?する時、各カラムの属性に問題があるのでしょうか?
winterboum

2020/08/30 05:44

implicit conversion of true into Integer は hash[true]=*** の部分で arry[true] と解釈されている、ということだと思うので、上の修正で通るかと思ったのですが。 エラーメッセージで行番号とかどの行であるとか出てませんか?
oiykram

2020/08/30 05:49

def create_list_from_csv(file) csv_users = [] CSV.foreach(file.path, headers: true) do |row| values = row.to_h csv_users << User.new( id: values["id"], name: values["name"], age: values["age"], ) csv_users users = csv_users.inject({true =>[],false => []}){ | res, user | res[user.valid?] << user } end ↑↑ 現在のコードは上記です。 エラーでは、 TypeError in UsersController#import no implicit conversion of true into Integer Extracted source (around line #22): | res, user | res[user.valid?] << user となってますね????????
winterboum

2020/08/30 05:56

users = csv_users.each_with_object({true =>[],false => []}){ | user,res | res[user.valid?] << user } だとどうでしょう
oiykram

2020/08/30 06:09

うまく、動作できました!!違いはなんだったのでしょうか??調べてみます。 users[true]とusers[false]できちんとvaildされているのも確認できました。 ///////////////////////////// [] [] [] [#<User id: 10, name: nil, age: 12, created_at: nil, updated_at: nil, note: "千葉", text: "え", number: 4444>] [#<User id: 10, name: nil, age: 12, created_at: nil, updated_at: nil, note: "千葉", text: "え", number: 4444>] [#<User id: 10, name: nil, age: 12, created_at: nil, updated_at: nil, note: "千葉", text: "え", number: 4444>] Redirected to http://localhost:3000/users Completed 302 Found in 15ms (ActiveRecord: 2.6ms | Allocations: 8563) ///////////////////////////// p users[false]をしたら、上記のように、コンソール上で、3回出てしまいましたが????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問