よしだです

プログラミングの勉強してます

belongs_toでclass_nameを指定した時の命名規則変化について

has_many throughでUserモデル、communities_usersモデル、Communityモデルがあった場合

 

中間モデルに

Belongs_to :user

Belongs_to :community

と書いた場合と

Belongs_to :user_id, class_name: "User"

Belongs_to :community_id, class_name: "Community"

と書いた場合で命名規則が変化するようです

 

具体的には

 

Belomgs_to :userと書いた場合、Userモデルでは

 

has_many :communities_users

has_many :communities, through: :communities_users, source: :community

と書けばいいですが

 

Belongs_to :user_id, class_name: "User"と書いた場合は

 

has_many :communities_users

has_many :communities, through: :communities_users, source: :community_id

と書く必要があるようです。

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と同じ高さのマージンをとると良いとのことです。

 

 

参考リンク

CSSだけで実装する!短めページの隙間なしフッタ – 株式会社AIコミュニケーション – 総合WEBプロモーション

form_forのtext_areaに入力された改行をそのまま表示する方法

text_areaで入力された文章を

<p><%= @user.comment %></p>

みたいな感じで表示されたら、改行が表現されなかったので、調べました。

 

どうやらsimple_formatなるメソッドを使えばいいようなので

<%= simple_format(@user.comment) %>

に変更。

 

結果うまく表示されました。良かったね。

renderの引数にインスタンス変数を与えたときの動作

この前、<%= render @products %>のようなコードは、与えられた変数と同じ名前のフォルダを探しにいくという趣旨の記事を書きましたが、微妙に違いました。

 

 

正確には、renderに与えられたモデルインスタンスと同じ名前のフォルダを探しにいくでした。

 

なので、Productモデルのインスタンスが中に入っていれば、変数の名前を@productsにしようが、@booksにしようが、@itemsにしようが、'views/products/product'を見つけにいくのです。

 

へえ〜

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: "更新時刻">

 

 

ん?

 

同じですね。

 

 

ちょっとググります。

 

 

 

...

 

 

 

結論からいうと、ありません。

user = User.first
user.comments.new
=> #<Comment id: nil, body: nil, user_id: 1, created_at: nil, updated_at: nil>
user.comments.build
=> #<Comment id: nil, body: nil, user_id: 1, created_at: nil, updated_at: nil>

昔は、newだと上のuser_idが入らなかったみたいですね。

今はどちらも入るようになっています。

そのため、慣習的に関連するモデルを生成するときは、buildを使うようです。 

 

qiita.com

 

 

ないんかい!!!!!!

 

まあ・・・今までの慣習に従って、関連付けされたデータを作る時は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')ということですね。

 

すっきり〜。ほんと色んな機能がありますなあ。