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

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

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

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

Q&A

解決済

1回答

3118閲覧

kaminariで条件をつけてpostを抽出したい

you88

総合スコア147

Ruby on Rails

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

0グッド

1クリップ

投稿2018/01/17 23:43

編集2018/01/18 11:05

kaminariを仕様していて特定のtagのitemを並べたいと考えています。

class TagController < ApplicationController def show @tag = Tag.find_by(id: params[:id]) tag_maps = TagMap.where(tag_id: params[:id]) @posts = Post.where(post_id: tag_maps.post_id).page(params[:page]).per(10).order('updated_at DESC') @description = @tag.tag_des end end

tagmapを中間に挟んでいてTagMap.where(tag_id: params[:id])でurlのtag_idからtagmap(tag_idとpost_idが一緒に格納されているrable)をwhereで検索。そのtagページに該当するpostをwhereで更に並べたいのですが下記のエラーが出ます

イメージ説明

kaminariで抽出するpostに条件をつけたい場合、どうすればいいんでしょうか?

class TagMap < ApplicationRecord belongs_to :post belongs_to :tag validates :post_id,presence:true validates :tag_id,presence:true end
class Post < ApplicationRecord has_many :tag_maps, dependent: :destroy has_many :tags, through: :tag_maps def save_posts(savepost_tags) current_tags = self.tags.pluck(:name) unless self.tags.nil? old_tags = current_tags - savepost_tags new_tags = savepost_tags - current_tags # Destroy old taggings: old_tags.each do |old_name| self.tags.delete Tag.find_by(name:old_name) end # Create new taggings: new_tags.each do |new_name| post_tag = Tag.find_or_create_by(name:new_name) self.tags << post_tag end end end
class Tag < ApplicationRecord validates :name,presence:true,length:{maximum:50} has_many :posts, through: :tag_maps has_many :tag_maps, dependent: :destroy end

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

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

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

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

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

scivola

2018/01/18 03:41

モデルの関連の定義を追記していただけますか
you88

2018/01/18 08:17

関係ありそうなtagmapの記述を追記しました。
scivola

2018/01/18 09:09

Post と Tag はどうですか。has_many とかしてませんか。
you88

2018/01/18 11:05

postとtagを追記しました!
guest

回答1

0

ベストアンサー

やりたいことはこうではないでしょうか。

rb

1def show 2 @tag = Tag.find(params[:id]) 3 @posts = @tag.posts.page(params[:page]).per(10).order('updated_at DESC') 4 @description = @tag.tag_des 5end

なお,エラーは kaminari とはまったく関係ありません。

勉強の指針

こんなことを勉強されるとよいかもしれません。

find

id の値が params[:id]) であるような Tag オブジェクトを得るのに

rb

1Tag.find_by(id: params[:id])

と書かれていますが,

rb

1Tag.find(params[:id])

と書きましょう。

Rails ガイドとかで find の使い方を読んでみてくださいね。

has_many, belongs_to

has_manybelongs_to が何のためにあるかをあまり理解なさっていないようですので,そこをしっかり勉強されるとよいと思います。

多対多だとややこしいので,一対多でちょっと説明します。

以下のようなモデルがあったとします。

rb

1class Book < ApplicationRecord 2 belong_to :publisher 3end 4 5class Publisher < ApplicationRecord 6 has_many :books 7end

書籍とその出版元を表しています。

Book のほうは,belongs_to :publisher があることによって,publisher というメソッド(など)が使えるようになります。
いま,book という変数に Book オブジェクトが入っているとします。
すると,book.publisher は,book が持つ publisher_id の値を id として持つ Publisher を検索し,そのモデルオブジェクトを作って返します。

一方,Publisher のほうは,has_many :books があることによって,books というメソッド(など)が使えるようになります。
いま publisher という変数に Publisher オブジェクトが入っているとします。
publisher.books の返り値は配列ではありません。
Book::ActiveRecord_Associations_CollectionProxy という,ちょっとややこしいものなのですが,イメージとしてはその出版社の書籍を集めた集合体,みたいに捉えて OK です。

publisher.books.count でその数を数えることができますし,

rb

1publisher.books.each do |book| 2 # なんとかかんとか 3end

のようにループを回すこともできますし,

rb

1publisher.books.pluck(:title)

でタイトル一覧を配列で得たりもできます。

また,

rb

1publisher.books.to_a

とすれば該当する Book オブジェクトの配列が得られます。

あらわに where(publisher_id: XXX) みたいな検索を書かなくてよいのです。それが ActiveRecord のパワーです。

これらも Rails ガイドや Rails チュートリアルを見てくださいね。

エラーメッセージの見方

直面したエラーから問題の所在を探る考え方を少し書きます。

エラーメッセージは

undefined method `post_id` for #<TagMap::ActiveRecord_Relation:0x007fa11fc17d08>

ですよね。

TagMap::ActiveRecord_Relation というクラスのインスタンスに post_id ってメソッドは無いけど?

といっています。

そしてエラーの出た行は

rb

1@posts = Post.where(post_id: tag_maps.post_id)...後略

です。

この行に post_id は二つしか出てきません。一つ目はハッシュのキーですから,メソッド呼び出しと解釈できるのは tag_maps.post_id しかありません。

post_id ってメソッドがあると思っていたのに無いってことは,tag_maps が思っていたオブジェクト(TagMap のインスタンス?)ではないってことですよね。

では,rails console で確かめてみましょう。コンソール上で

TagMap.where(tag_id: 123).class

と打ちます。tag_id の値には,念のため実在するものを入れてください。
また,オブジェクトのクラスを確認するには必ず .class をつけます。
というのは,コンソール上では式の評価結果を画面表示可能にするために暗黙にあるメソッド呼び出しが行われて,式の評価結果とは違うクラスのオブジェクトが表示されることがあるからです。

さて,そうすると,この tag_maps が実は TagMap::ActiveRecord_Relation のインスタンスだったということが分かります。
TagMap.where の結果って,配列とかじゃなくて,そういうややこしいクラスのインスタンスなんですよ。
こいつは,さらに where を呼び出したり,いろいろできます。
このインスタンスが生成された時点では,検索はまだ行われていません。

いずれにせよ,こいつは TagMap オブジェクトと違って,post_id なんてメソッドは持っていません。

とまあ,このあたりまでは自力でもいろいろ調べられます。

投稿2018/01/18 13:13

scivola

総合スコア2108

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

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

you88

2018/01/18 15:27

丁寧にありがとうございます。 すみません、いただいたコードで動かしたら下記のエラーが出てしまい Cannot have a has_many :through association 'Tag#posts' which goes through 'Tag#tag_maps' before the through association is defined.
scivola

2018/01/18 15:47

いま気付きましたが,Tag モデルの定義で,has_many :tag_maps よりも先に has_many :posts しているのが原因でしょう。 中間テーブルのほうを先に書かなければなりません。
you88

2018/01/18 22:37

ありがとうございます。 通りました! 下記勉強になりました。 ・throughで関連付けする場合、間に入るmodelを先に定義する ・関連付けをすると〜_id部分を〜メソッドで呼び出せる ・whereは配列ではないのでうまく渡せていなかった
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問