dxo
dxo

Reputation: 33

Rails:Couldn't find all Post with 'id': (false, true) (found 1 results, but was looking for 2)

I am building a system for category search. At that time, the following errors are occurring in the current system.

Couldn't find all Post with 'id': (false, true) (found 1 results, but was looking for 2).

Controllers

def category
  @category = params[:category_name]
  @posts=Post.find(PostCategory.group(:post_id).pluck(:@category))
  #↑The error point.
end

routes

get '/category/:category_name', to: 'pages#category'

Views

<li><a href="category/casual">casual</a></li>
<li><a href="category/natural">natural</a></li>
<li><a href="category/clean">clean</a></li>
<li><a href="category/rock">rock</a></li>
<li><a href="category/formal">formal</a></li>
<li><a href="category/street">street</a></li>
<li><a href="category/hip_hop">hip_hop</a></li>
<li><a href="category/sports">sports</a></li>
<li><a href="category/outdoors">outdoors</a></li>
<li><a href="category/surf">surf</a></li>

Relationship of models

belongs_to :post
has_one :post_category
accepts_nested_attributes_for :post_category

PostCategory table

create_table "post_categories", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "casual", default: false, null: false
    t.boolean "natural", default: false, null: false
    t.boolean "clean", default: false, null: false
    t.boolean "rock", default: false, null: false
    t.boolean "formal", default: false, null: false
    t.boolean "street", default: false, null: false
    t.boolean "hip_hop", default: false, null: false
    t.boolean "sports", default: false, null: false
    t.boolean "outdoors", default: false, null: false
    t.boolean "surf", default: false, null: false
    t.integer "post_id"
    t.index ["post_id"], name: "index_post_categories_on_post_id"
  end

What I want to do

Example. I want to find a column where category_name params(casual) and PostCategory column(casual) are the same, and from there, find only the ones that are true, and retrieve all the records of Post whose post_id corresponds to PostCategory column(casual, only true) and reflect them in the view.

Thank you.

Upvotes: 0

Views: 238

Answers (3)

Priyanka
Priyanka

Reputation: 59

@posts=Post.find(PostCategory.group(:post_id).pluck(:category))

Upvotes: 0

Dennis Hackethal
Dennis Hackethal

Reputation: 14295

First, there's a problem in your category method where it says :@category. That would probably need to say @category (without the colon), so that the method body's second line reads:

@posts=Post.find(PostCategory.group(:post_id).pluck(@category))
                                                    ^

That aside, it seems that you're trying to fetch all posts that belong to a certain category. What you're doing right now won't work because your query returns an array of booleans, and then you use booleans to look up posts. But you'd need ids to look up posts because you can't pass booleans to Post.find.

Why not just do (not tested):

@posts = Post.find(PostCategory.where(@category => true).pluck(:post_id))

or, in a single query (not tested either):

@posts = Post.joins(:post_category).where("post_categories.#{@category}" => true)

But for that last one you may need to check the @category variable against a whitelist to avoid injections. EDIT: As Max suggests in the comments, you may want to change the where clause to where( post_categories: { @category => true }) to avoid injections.

Upvotes: 1

max
max

Reputation: 102343

A better solution which doesn't require you to modify the database to add categories would be to just setup a more sane normalization table / join table setup:

# rails g model category name:string:uniq
class Category < ApplicationRecord
  has_many :categorizations
  has_many :posts, through: :categorizations

  validates :name, 
     uniqueness: true,
     presence: true
end

# rails g model categorization post:belongs_to category:belongs_to
class Categorization < ApplicationRecord
  belongs_to :category
  belongs_to :post

  validates :post_id, 
     uniqueness: { scope: :category_id }
end

class Post < ApplicationRecord
  has_many :categorizations
  has_many :categories, through: :categorizations
end

The Rails to handle this would either to handle this on the categories#show action (or with a nested route):

resources :categories
class CategoriesController < ApplicationController
  def show
    @category = Category.find_by!(name: params[:id])
    @posts = @category.posts 
  end
end

This of course implies that you have some sort of slugging in place that ensures that the name of the category can be used in the path of a URL. Gems like FriendlyID are your friend here.

Upvotes: 1

Related Questions