railsのアソシエーションでAssociationTypeMismatchエラーが出てハマった
データベースのリレーションが苦手です。吉田です。
railsのアソシエーションで軽くハマったのが解決したので、メモ。
今回の原因は命名規則が間違っていてエラーが起こりました。
作りたかったのは、ユーザーはたくさんのコミュニティを持っていて、コミュニティもたくさんのユーザーを持っている関係。
そして、コミュニティは一人の管理ユーザーに従属している関係です。
とりあえずモデルを作ります。
Userは
id
name
Communityは
id
name
admin_user
中間テーブルのCommunity_usersに(名前付け間違えました)
user_id
community_id
これでUserモデルに
has_many :community_users
has_many :communities, through: :community_users, source: :community_id
has_many :admin_circles, class_name: "Circle", foreign_key: "admin_user"
Community_usersモデルに
belongs_to :user_id, class_name: "User"
belongs_to :community_id, class_name: "Community"
Communityモデルに
has_many :community_users
has_many :users, through: :community_users, source: :user_id
belongs_to :admin_user, class_name: "User"
これで、僕の予想では
@user.admin_circles.buildみたいにやればadmin_userカラムにユーザーのIDが入った状態でbuild出来ると思ったのですが、タイトルのAssociationTypeMismatchというエラーを吐きました。
軽くググると、railsの命名規則に適合していないと出てくるエラーらしいので、適合してなさそうな所を探す。
とりあえずCommunityモデルのadmin_userカラムをadmin_user_idに変更して、
Userモデルの
has_many :admin_circles, class_name: "Circle", foreign_key: "admin_user"
を
has_many :admin_circles, class_name: "Circle", foreign_key: "admin_user_id"
に変更。
もう一度@user.admin_circles.buildをやると無事成功。
ソースコードとか読んでもうちょっと理解深めたいですな。
footerを最下部へ設置する方法
空のページのようにコンテンツがない状態でもfooterを画面の一番下に設置する方法(fixedではなく)はないかなと思い調べました。
すると
html {
position: relative;
min-height: 100%;
}
footer {
position: absolute;
bottom: 0;
}
とすると可能なようですね。
これにプラスしてページ全体にfooterと同じ高さのマージンをとると良いとのことです。
参考リンク
form_forのtext_areaに入力された改行をそのまま表示する方法
text_areaで入力された文章を
<p><%= @user.comment %></p>
みたいな感じで表示されたら、改行が表現されなかったので、調べました。
どうやらsimple_formatなるメソッドを使えばいいようなので
<%= simple_format(@user.comment) %>
に変更。
結果うまく表示されました。良かったね。
buildとnewの違いとは・・・。
buildとnew・・・何が違うんだ・・・。
ということで調べます。
まず、newに関しては説明はいらないと思いますが、クラスのインスタンスを作成するメソッドですね。
@user = User.new(name: "Taro")
=> #<User id: nil, name: "Taro", created_at: nil, updated_at: nil >
@user.save
=> #<User id: 1, name: "Taro", created_at: "作成時刻", updated_at: "更新時刻">
となります。
さて、buildは2つのデータベースを関連させるときに現れます。
なので2つのデータベースを用意します。
UserモデルとBookモデルです。
UserはたくさんのBookを持ち。
Bookは一人のUserに所有されているような状態にします。
Userモデルに
has_many :books
Bookモデルに
belongs_to :user
ですね。
まず、ユーザーを作成します。
user1 = User.new(name: "Taro")
user1.save
=> #<User id: 1, name: "Taro", created_at: "作成時刻", updated_at: "更新時刻">
ここまでは先ほどと同じです。
次に、 ユーザーに所有されているBookを作成します。
book1 = user1.books.build(title: "1")
=> #<Book id: nil, user_id: 1, title: "1", created_at: nil, updated_at: nil>
book1.save
=> #<Book id: 1, user_id: 1, title: "1", created_at: "作成時刻", updated_at: "更新時刻">
紐付けされて作成されました。
次に、newで作ってみましょう
タイトルだけ変えます。
book2 = user1.books.new(title: "2")
=> #<Book id: nil, user_id: 1, title: "2", created_at: nil, updated_at: nil>
book1.save
=> #<Book id: 2, user_id: 1, title: "2", created_at: "作成時刻", updated_at: "更新時刻">
ん?
同じですね。
ちょっとググります。
...
結論からいうと、ありません。
昔は、newだと上のuser_idが入らなかったみたいですね。
今はどちらも入るようになっています。
そのため、慣習的に関連するモデルを生成するときは、
build
を使うようです。
ないんかい!!!!!!
まあ・・・今までの慣習に従って、関連付けされたデータを作る時はbuildを使いましょう。
以上!!!!
renderに@○○を渡したときの動作について
railsでまたいまいち理解できないところにつまづきました。
<%= render @products %>
というコード。
これは、
<%= render partial: 'products/product', collection: @products %>
を略して書いたコードなんですが、なんでここまで略せるのかがよく理解できませんでした。
なんで変数を渡しただけでpartialの場所まで指定できるんだ・・・?
と、思い色々テストしてみました。
まず疑問①'_product.html.erb'が別のフォルダに入ってたらどうなるの?
とりあえず別のフォルダに入れてリロードすると、MissingTempleteErrorが出ました。おお。
@productsと同じ名前のフォルダに探しにいくようです。(この場合は'views/products')
疑問②ファイル名も'products'(複数形)にしても平気?
正直①でだいたい納得したんですが一応これも調べておきます。
最後にsをつけてリロードしてみると、やはりMissingTempleteErrorが。
疑問③フォルダ名を'product'(単数形)にしたら?
さらにもういっちょ。
フォルダ名を変更してリロード・・・。
MissingTempleteErrorです。
つまり、<%= render @products %>というような省略形を使うと
まず、与えられた変数と同じ名前のフォルダを探しにいき(この場合は'views/products')
フォルダが見つかれば、与えられた変数の単数系のファイルを探す(この場合は'views/products'の中の'product.html.erb')ということですね。
すっきり〜。ほんと色んな機能がありますなあ。