简体   繁体   中英

Rails searching a has_many relationship

Heyo. Been trying to figure this out but I've been stuck too long and it's just getting painful!

I'm trying to do an Advanced Search form allowing you to search for Users based off settings that are in other models. eg Search for a User named Jim, who does Running, and has weight loss as his goal.

I have three models:

  • User (using Devise)
  • Sport (many-to-many with user)
  • Goals (user has_many goals, goal belongs_to user)

So far I have managed to get it working so I can search for things in the User model (such as name) and also for Users Sports through a select box. What I haven't been able to get working is searching for the Users goals and I don't get why.

What I get is "Nobody seems to have these preferences" when searching ONLY for Goals and no other fields.

I have tried using the same code as my Sports but that didn't work (guessing because of the different relationships?)

# searches/show.html.erb
<% if @search.search_users.empty? %>

  <p>Nobody seems to have these preferences</p>

<% else %>
<% @search.search_users.each do |u| %>
  <tr>
   <td><%= u.name %></td>

    <% u.sports.each do |s| %>
     <td><%= s.name %></td>
    <% end %>

    <% u.goals.each do |g| %>
     <td><%= g.name %></td>
    <% end %>

  </tr>
<% end %>

I've done associations in the console and when I type for example u.goals I get this (and the opposite when I query what users are associated with a goal):

irb(main):015:0> u.goals
=> #<ActiveRecord::Associations::CollectionProxy [#<Goal id: 1, name: "Weight Loss", user_id: 1>, #<Goal id: 3, name: "Strength", user_id: 1>]>

Here's my current code:

# user.rb
class User < ApplicationRecord
 has_and_belongs_to_many :sports
 has_many :goals, :foreign_key => :goal_id
end

# sport.rb
class Sport < ApplicationRecord
 has_and_belongs_to_many :users
end

# goal.rb
class Goal < ApplicationRecord
 belongs_to :user, :foreign_key => :goal_id
end

And my searches stuff:

# search.rb
def search_users
 users = User.all
 users = users.where("users.name ILIKE ?", "%#{keywords}%") if  keywords.present?
 users = users.joins(:sports).where("sports.name ILIKE ?", "%#{name}%") if name.present?
 users = users.where(goal_id: goal_id) if goal_id.present?
 return users
end

# searches/new.html.erb
<%= form_for @search do |s| %>
  <div class="form-group">
    <%= s.label :keywords %>
    <%= s.text_field :keywords %>
  </div>

  <div class="form-group">
    <%= s.label :exercise %>
    <%= s.select :name, options_for_select(@s_names), include_blank: true %>
  </div>

  <div class="form-group">
    <%= s.label :goals %>
    <%= s.collection_select :goal_id, Goal.order(:name), :id, :name, include_blank: true %>
  </div>
  <%= s.submit "Search", class: "btn btn-primary" %>
<% end %>

# searches_controller.rb
class SearchesController < ApplicationController
  def new
    @search = Search.new
    @s_names = Sport.uniq.pluck(:name)
    @users = User.uniq.pluck(:name)
  end

   def create
    @search = Search.create(search_params)
    redirect_to @search
  end

  def show
    @search = Search.find(params[:id])
  end

  private
  def search_params
   params.require(:search).permit(:keywords, :name, :goal_id)
 end
end

and then my schema for reference:

create_table "goals", force: :cascade do |t|
    t.string  "name"
    t.integer "user_id"
    t.index ["user_id"], name: "index_goals_on_user_id", using: :btree
  end

  create_table "searches", force: :cascade do |t|
    t.string   "keywords"
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "goal_id"
    t.index ["goal_id"], name: "index_searches_on_goal_id", using: :btree
  end

  create_table "sports", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "sports_users", id: false, force: :cascade do |t|
    t.integer "user_id",  null: false
    t.integer "sport_id", null: false
    t.index ["user_id", "sport_id"], name: "index_sports_users_on_user_id_and_sport_id", using: :btree
  end

  create_table "users", force: :cascade do |t|
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet     "current_sign_in_ip"
    t.inet     "last_sign_in_ip"
    t.datetime "created_at",                          null: false
    t.datetime "updated_at",                          null: false
    t.string   "name"
    t.integer  "movement_id"
    t.integer  "goal_id"
    t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
    t.index ["goal_id"], name: "index_users_on_goal_id", using: :btree
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
  end

  add_foreign_key "goals", "users"
end

Extremely sorry for the huge amount of messy code but I'm just tripping over myself at this point and getting confused.

Thank you greatly in advance.

I would change

users = users.where(goal_id: goal_id) if goal_id.present?

To

users = users.joins(:goals).where(goals: {id: goal_id})

For advanced searching I've recently used approach presented in this article: http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/ and I think it's worth reading if you think about expanding search options.

EDIT: full response in comments below :foreign_key => :goal_id needed to be removed

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM