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

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

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

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

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

Q&A

解決済

2回答

420閲覧

unknown attribute 'XXX' for XXX. で必要ないはず/存在するはずのカラムが要求される(最終更新: 210810)

kaoru-drosera

総合スコア23

Ruby on Rails 6

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

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

0グッド

0クリップ

投稿2021/08/09 02:32

編集2021/08/11 03:45

前提

クリック/キータッチで効果音を鳴らすことができるサンプラーアプリを作っています。

ここで作っているアプリはこれに加えて好きな音源をフォームで読み込んで音量やループ可否なども設定できます。
さらに、ログイン中は変更した設定を保存して、使いたい時に保存した設定を読み込むことができます。

イメージとしては「効果音ラボ」の「効果音ポン出し」に保存機能がついたようなものです。もっと具体的に言うと「KLANG」に当たると思われます。

効果音ラボ 効果音ポン出し
https://soundeffect-lab.info/pon/app.html

KLANG
http://m-tank.info/common/klang

実現したいこと

マウスクリック/キータッチで効果音を鳴らせるボタンが12あり、
12のボタンごとに「使用する効果音の音源(とその名前)」と「ボタンごとの設定(どのキーに配置するか、音量、ループ可能かどうか、ボタンの色)」を読み込みます。
それら12ボタンごとの設定と読み込む音源を、「サンプラー」の一つの投稿として保存(作成)する仕組みです。

##使用するテーブル
使用するテーブルは以下の通りです。
ユーザー(User)もありますが今回は省略いたします。
ちなみに簡単に言うとログインはdeviseで、メールアドレスではなくユーザーネーム(半角英数字)とパスワードでログインする仕組みにしています。

サンプラー(Sampler)
|保存するもの|カラム名|データ型|必須?|
|:--|:--|:--|
|id|id|-|
|ユーザーid|user_id|bigint?|
|サンプラー名|sampler_name|string|

ボタンごとの設定(Seboard)
|保存するもの|カラム名|データ型|必須?|
|:--|:--|:--|
|id|id|-|
|サンプラーid|sampler_id|bigint|true|
|効果音ライブラリid|sefile_id|bigint?|
|キー配置|position|integer|
|ボタン色|btncoler|integer|
|音量|volume|integer|
|ループ可否|loopable|loopable|

効果音ライブラリ(Sefile)
|保存するもの|カラム名|データ型|必須?|
|:--|:--|:--|
|id|id|-|
|ユーザーid|user_id|bigint|
|ボタンごとの設定のid|seboard_id|bigint|
|効果音(をDBに保存する際)の名前|sename|string|
|効果音の音源データ|sedata|(binary)string|

「効果音の音源」を「ボタンごとの設定」と分けているのは、「音源」は「サンプラー」の一つの投稿として投稿する以外にも直接「音源」を投稿できる仕組みにしたいためです。
証拠に、user_idのカラムがあり、idを取得してリレーションできるようにしています。

投稿(保存)のしくみ

投稿のしくみ

一度の投稿で更新されるsampler一つにつき12のseboardがあり、それぞれに一つずつ(空白可)sefileが割り当てられます。

HTML上でのフォームで送る値は以下のような感じになります。

{"authenticity_token"=>"[FILTERED]", "sampler"=> {"seboards_attributes"=> {"1"=>{"position"=>"1", "volume"=>"50", "btncolor"=>"1", "loopable"=>"0", "sefile_attributes"=>{"sedata"=>"click.mp3", "sename"=>""}}, "2"=>{"position"=>"2", "volume"=>"50", "btncolor"=>"1", "loopable"=>"0", "sefile_attributes"=>{"sedata"=>"click2.mp3", "sename"=>""}}}}, "sampler_name"=>"サンプラー名", "commit"=>"保存する"}

新規作成/更新のしくみ

ボタンは一つに統一し、「新規作成/更新」機能は一つに統一します。
具体的に言うと以下の通りです。

「『サンプラーに付ける名前』に前回保存したサンプラーの名前と同じ投稿がある場合、その投稿に今回保存するサンプラーの設定を更新。
それ以外は新規作成とする。」

以上、基本的な保存の仕組みに加えて、もう一つ

「『ボタンごとの設定』で読み込む音源に、
『効果音ライブラリ』の「効果音の名前」「効果音の音源データ」両方の値が同じデータがある場合、そのデータのidを『ボタンごとの設定』の『効果音ライブラリid』に割り当てる。
ない場合は新規データとして『効果音ライブラリ』のデータとして保存。そのidを割り当てる」

が加わります。

詰まっている箇所(更新:210810)

イメージ説明

**「unknown attribute 'XXX' for XXX.」**なのですが、存在しない、あるいは存在する必要がない要素を要求されている状態です。
ただ、外部キーなどリレーションに関わる問題であることには間違いないとは思います。

#####(更新: 210810)

maisumakun様、ご回答ありがとうございます。
sefileに、seboard_idを追加いたしました。
しかしその結果、以下のようなエラーが出ています。

イメージ説明

「sefile_id」、要するにSefileクラスでは存在するはずのクラスがない、ということになると思われます。
Sefileもクラスである以上、idが存在しないとは考えにくいです。
今の所、こればかりは不可解です。

現在のコード

/app/models/sampler.rb

ruby

1class Sampler < ApplicationRecord 2 # リレーション 3 belongs_to :user, class_name: "User", foreign_key: 'user_id' 4 5 has_many :seboards#, class_name: "Seboard", foreign_key: 'id' 6 accepts_nested_attributes_for :seboards 7 8 has_one :sefile, :through => :seboards#, foreign_key: 'sefile_id' 9 accepts_nested_attributes_for :sefile 10 11 # バリデーション(予定) 12 13 14end
/app/controllers/sampler_controller.rb

ruby

1class SamplerController < ApplicationController 2 3 before_action :authenticate_user! 4 5 # 投稿を一覧形式にする(予定) 6 def seindex 7 @samplers = Sampler.all 8 end 9 10 def save 11 @sampler = Sampler.find_or_initialize_by(sampler_name: params[:sampler_name]) 12 end 13 14 def submit 15 @sampler = Sampler.find_or_initialize_by(sampler_name: params[:sampler_name]) 16 # sampler_sefile = Sampler.seboard.sefile.find_or_initialize_by(sename: params[:sename], sedata: params[:sedata]) 17 unless @sampler.persisted? 18 @sampler = Sampler.new(sampler_params) 19 @sampler.user_id = current_user.id 20 21 unless @sampler.save 22 flash[:danger] = "保存に失敗しました" 23 else 24 flash[:success] = "保存しました" 25 end 26 27 else 28 @sampler = Sampler.update_attrbutes!(update_sampler_params) 29 # @sampler.save 30 31 unless @sampler.save 32 flash[:danger] = "更新に失敗しました" 33 else 34 flash[:success] = "更新しました" 35 end 36 37 end 38 39 redirect_to root_path 40 41 end 42 43 44 def show 45 @sampler = Sampler.find(params[:id]) 46 end 47 48 private 49 50 def sampler_params 51 params.require(:sampler).permit(:sampler_name, seboards_attributes: [:position, :btncolor, :volume, :loopable, sefile_attributes: [:sename, :sedata]]) 52 end 53 54 def update_sampler_params 55 params.require(:sampler).permit(:sampler_name, seboards_attributes: [:position, :btncolor, :volume, :loopable, sefile_attributes: [:sename, :sedata, :id]]) 56 end 57 58 59end
/app/views/sampler/_save.html.erb

html

1 # 字数制限につき一時的に削除 申し訳ありません
/app/models/seboard.rb

ruby

1class Seboard < ApplicationRecord 2 belongs_to :sampler 3 4 has_one :sefile#, class_name: "Sefile", foreign_key: 'id' 5 # ↑ 6 # has_many :sefiles にするとうまくいった。 7 # この調子だと下の式のものやviewとcontrollerに点在するsefileの設定も複数形にするといいのだろうが、こちらを単数形にしたい。 8 accepts_nested_attributes_for :sefile 9 # accepts_nested_attributes_for :sefiles 10 11end
/app/models/sefile.rb(更新:210810)

ruby

1class Sefile < ApplicationRecord 2 belongs_to :user, class_name: "User", foreign_key: 'user_id' 3 belongs_to :seboard, optional: true 4 5 mount_uploader :sedata, SeUploader 6 7 # バリデーションはこれ以降に 8 9end 10
/db/schema.rb(更新:210810)

sefileに、カラム「seboard_id」を追加いたしました。

ruby

1() 2 3ActiveRecord::Schema.define(version: 2021_08_09_172009) do 4 5 # These are extensions that must be enabled in order to support this database 6 enable_extension "plpgsql" 7 8 create_table "samplers", force: :cascade do |t| 9 t.bigint "user_id", null: false 10 t.string "sampler_name" 11 t.datetime "created_at", precision: 6, null: false 12 t.datetime "updated_at", precision: 6, null: false 13 t.index ["user_id"], name: "index_samplers_on_user_id" 14 end 15 16 create_table "seboards", force: :cascade do |t| 17 t.bigint "sampler_id", null: false 18 t.bigint "sefile_id" 19 t.integer "position" 20 t.integer "btncolor" 21 t.integer "volume" 22 t.boolean "loopable" 23 t.datetime "created_at", precision: 6, null: false 24 t.datetime "updated_at", precision: 6, null: false 25 t.index ["sampler_id"], name: "index_seboards_on_sampler_id" 26 t.index ["sefile_id"], name: "index_seboards_on_sefile_id" 27 end 28 29 create_table "sefiles", force: :cascade do |t| 30 t.bigint "user_id", null: false 31 t.string "sename" 32 t.string "sedata" 33 t.datetime "created_at", precision: 6, null: false 34 t.datetime "updated_at", precision: 6, null: false 35 t.bigint "seboard_id" 36 t.index ["seboard_id"], name: "index_sefiles_on_seboard_id" 37 t.index ["user_id"], name: "index_sefiles_on_user_id" 38 end 39 40 create_table "users", force: :cascade do |t| 41 t.string "user_name" 42 t.datetime "created_at", precision: 6, null: false 43 t.datetime "updated_at", precision: 6, null: false 44 t.string "password_digest" 45 t.string "email", default: "", null: false 46 t.string "encrypted_password", default: "", null: false 47 t.string "reset_password_token" 48 t.datetime "reset_password_sent_at" 49 t.datetime "remember_created_at" 50 t.index ["email"], name: "index_users_on_email", unique: true 51 t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 52 end 53 54 add_foreign_key "samplers", "users" 55 add_foreign_key "seboards", "samplers" 56 add_foreign_key "seboards", "sefiles" 57 add_foreign_key "sefiles", "users" 58end 59 60

補足情報(FW/ツールのバージョンなど)

ブラウザ:chrome(最新のバージョン)
テキストエディタ:atom(ver:1.32.2)
rails: Rails 6.1.4

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

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

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

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

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

guest

回答2

0

ベストアンサー

存在しない、あるいは存在する必要がない要素を要求されている状態です。

そのとおりです。Sefilebelongs_to :seboardとするには、sefiles.seboard_id列が必要です。

投稿2021/08/09 02:36

maisumakun

総合スコア146018

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

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

kaoru-drosera

2021/08/09 08:00

ご回答ありがとうございます。 ただし、sefile(効果音の音源)はseboardに依存させたくないと考えています。 理由は、samplerからの投稿以外にsefileとして直接投稿できる仕組みにしたいのと、投稿したsampler以外のseboardでも利用出来るようにしたいためです。 そんな時もやはりseboard_idは必要なのでしょうか? そうすると、「値が空白/複数持ちでも構わない」といった設定にすべきと考えているのですが合っていますでしょうか?
maisumakun

2021/08/09 08:04 編集

「belongs_to :seboard, optional: true」とすれば、関連付けはあってもなくてもいい、という形になります(データベースのseboard_id列は必要です)。
kaoru-drosera

2021/08/09 08:07

お早いご回答、とても助かります。 参考にさせていただきます。
kaoru-drosera

2021/08/09 18:03

ご回答、誠にありがとうございます。 大変申し訳ありませんが、また新しく問題が出てきてしまいました。 出てきた問題を質問内容に盛り込みましたので、お手隙の際ご協力いただけると幸いです。
guest

0

最終?更新(210811)

前回(210811)更新したエラーが解決致しました。
samplerとsefileのモデルのリレーションに「inverse_of」を追加することで、データ送信時の「ActiveModel::unknownAttribute~」のエラーを消すことができました。

/app/models/sampler.rb

ruby

1 belongs_to :user, class_name: "User", foreign_key: 'user_id' 2 3 has_many :seboards, inverse_of: :sampler#, class_name: "Seboard", foreign_key: 'seboard_id' 4 accepts_nested_attributes_for :seboards 5 6 7 has_one :sefile, :through => :seboards#, foreign_key: 'sefile_id' 8 accepts_nested_attributes_for :sefile 9
/app/models/sefile.rb

ruby

1 belongs_to :user, class_name: "User", foreign_key: 'user_id' 2 3 belongs_to :seboard,inverse_of: :sefile, optional: true 4 5

質問した問題の解決はできましたが、controller側で問題があり投稿機能の実装とはなっていません。それどころか現在詰まっており、また同じアプリの件で質問をさせていただく予定です。
ただし、タイトルで質問していることと主旨が外れてくるためここで締めとさせていただきます。

ご迷惑をおかけいたしました。
ご協力していただき、本当にありがとうございました。自己解決ではありますが、回答者をベストアンサーといたします。

投稿2021/08/11 03:43

編集2021/08/11 03:46
kaoru-drosera

総合スコア23

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問