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

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

ただいまの
回答率

91.03%

  • Ruby on Rails

    6179questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 150

you88

score 68

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
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • scivola

    2018/01/18 12:41

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

    キャンセル

  • you88

    2018/01/18 17:17

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

    キャンセル

  • scivola

    2018/01/18 18:09

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

    キャンセル

  • you88

    2018/01/18 20:05

    postとtagを追記しました!

    キャンセル

回答 1

checkベストアンサー

+2

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

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

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

 勉強の指針

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

 find

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

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

と書かれていますが,

Tag.find(params[:id])

と書きましょう。

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

 has_many, belongs_to

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

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

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

class Book < ApplicationRecord
  belong_to :publisher
end

class Publisher < ApplicationRecord
  has_many :books
end

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

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 でその数を数えることができますし,

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

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

publisher.books.pluck(:title)

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

また,

publisher.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 ってメソッドは無いけど?

といっています。

そしてエラーの出た行は

@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/19 00:27

    丁寧にありがとうございます。
    すみません、いただいたコードで動かしたら下記のエラーが出てしまい

    Cannot have a has_many :through association 'Tag#posts' which goes through 'Tag#tag_maps' before the through association is defined.

    キャンセル

  • 2018/01/19 00:47

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

    キャンセル

  • 2018/01/19 07:37

    ありがとうございます。
    通りました!
    下記勉強になりました。

    ・throughで関連付けする場合、間に入るmodelを先に定義する
    ・関連付けをすると〜_id部分を〜メソッドで呼び出せる
    ・whereは配列ではないのでうまく渡せていなかった

    キャンセル

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

  • ただいまの回答率 91.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby on Rails

    6179questions

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