Rails - filtering multiple tables with scope - ruby-on-rails

I'm doing an online word learning project and i want to making a search feature where you can filter word by lessons, words and learning status (learned/unlearned). Filter by lessons and words are done. I'm stuck at the last filter condition. I'm using scope and the params for learn status just can't seems to get to the scope, the "words" table and "user_words" table won't join (i tried with these two tables first).
Here are my tables:
create_table "users" do |t|
t.string "username"
end
create_table "words" do |t|
t.string "name"
t.integer "lesson_id"
end
create_table "user_words" do |t|
t.integer "user_id"
t.integer "word_id"
t.boolean "status"
end
My models:
class User < ApplicationRecord
has_many :user_words
end
class Word < ApplicationRecord
has_many :user_words
scope :order_name_asc, -> {order name: :asc}
scope :search_prefix, -> prefix {where "name like ?", "#{prefix}%"}
scope :search_lesson, -> lesson_id {
where("lesson_id = ?", lesson_id) if lesson_id.present?}
# Is the problem here?
scope :search_learned, -> (learned) do
joins(:user_words).where("user_words.status = ?",
learned[:user_word_status].to_i) if learned[:user_word_status]
end
end
class UserWord < ApplicationRecord
belongs_to :user
belongs_to :word
end
My controller
class StaticPagesController < ApplicationController
before_action :load_lessons, only: :home
before_action :load_user_words, only: :home
def home
#words = Word.search_learned(learned: params[:learned]) #or the problem's here
.search_lesson(params[:lesson_id])
.search_prefix(params[:prefix]).order_name_asc
.paginate page: params[:page]
end
end
And the view:
# Lesson filter option:
<%= select_tag :lesson_id,
grouped_options_for_select(#categories.map {|category|
[category.name,category.lessons.map {|lesson| [lesson.name,
lesson.id]}]}),
include_blank: t(".all_lessons"), class: "btn btn-primary" %>
# Learning status filter option (is the problem here?):
<%= select_tag :learned,
options_for_select(#user_words.map(&:status)),
include_blank: t(".learned"), class: "btn btn-secondary" %>
# Text input fied, submit button and the word list:
<%= text_field_tag :prefix, params[:prefix],
placeholder: t(".search"), class: "form-control" %>
<%= submit_tag t(".search"), name: nil, class: "btn btn-success" %>
<% #words.each do |word| %>
<%= link_to word.name, "#" %>
<% end %>

Related

Rails form : AssociationTypeMismatch in form with select

I try to make a connection between a Match and 2 Players in a form where we can select the players from the list of the players registered:
<%= f.label :"Joueur 1" %>
<%= f.select :playerone, #players.collect {|a| [a.name, a.id]} , class: 'form-control' %>
<%= f.label :"Joueur 2" %>
<%= f.select :playertwo, #players.collect {|b| [b.name, b.id]} , class: 'form-control' %>
<%= f.label :Prolongations %>
<%= f.check_box :prolongations %><br />
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
The schema of tables : with a Join Table Matches Players
create_table "matches", force: :cascade do |t|
t.boolean "prolongations"
end
create_table "matches_players", id: false, force: :cascade do |t|
t.integer "match_id", null: false
t.integer "player_id", null: false
t.index ["match_id", "player_id"], name: "index_matches_players_on_match_id_and_player_id"
t.index ["player_id", "match_id"], name: "index_matches_players_on_player_id_and_match_id"
end
create_table "players", force: :cascade do |t|
t.string "name"
t.integer "points"
end
In matches.controller :
class MatchesController < ApplicationController
attr_accessor :player_id, :playerone, :playertwo
def new
#match= Match.new
#players = Player.all
end
def create
#match = Match.new(match_params)
#players = Player.all
if #match.save
flash[:success] = "Votre match a bien été enregistré !"
redirect_to #match
else
render 'new'
p "Une erreur existe, veuillez recommencer."
end
end
def show
#match = Match.find(params[:id])
end
private
def match_params
params.require(:match).permit(:prolongations, :playerone, :playertwo)
end
end
And in the Match model :
class Match < ApplicationRecord
has_many :teams , class_name: "Team"
belongs_to :playerone, class_name: "Player" ,foreign_key: "player_id"
belongs_to :playertwo, class_name: "Player" ,foreign_key: "player_id"
end
And the result when I submit my form is :
Player(#69852298534200) expected, got "1" which is an instance of String(#4817000)
{"utf8"=>"✓",
"authenticity_token"=>".............",
"match"=>{"playerone"=>"1", "playertwo"=>"3", "prolongations"=>"0"},
"commit"=>"Enregistrer le match"}
How can I solve it ?
The easiest fix is just to use playerone_id and playertwo_id as the param keys (change the name of the inputs). If you use playerone the setter expects the argument to be an instance of Player - not a string containing an id.
Which will fix the immediate issue but there is a much better way to solve it.
Start by setting up a real many to many association:
class Match < ApplicationRecord
has_many :player_matches, dependent: :destroy
has_many :players, through: :player_matches
end
class Player < ApplicationRecord
has_many :player_matches, dependent: :destroy
has_many :matches, through: :player_matches
end
class PlayerMatches < ApplicationRecord
belongs_to :player
belongs_to :match
end
This lets you avoid a very awkward issue when you set two belongs to associations to the same table where the associated record can be in either column:
Match.find_by("(playerone_id = :a OR playertwo_id = :a) AND (playerone_id = :b OR playertwo_id = :b)", a: a, b: b)
Yeah thats how you have to query for a match between players. Joins can be even more messy.
With that setup you can simply assign players to a match by:
#match.player_ids = [1,2]
Which is exactly what you can do with the collection helpers:
<%= form_for(#match) do |f| %>
<div class="field">
<%= f.label :player_ids, "Select the players" %>
<%= f.collection_select :player_ids, Player.all, :id, :name, multiple: true %>
</div>
<% end %>
All you have to do on the controller side is whitelist player_ids:
class MatchesController < ApplicationController
# ...
def create
#match = Match.new(match_params)
# ...
end
def update
if #match.update(match_params)
# ...
else
# ...
end
end
# ...
private
def match_params
params.require(:match).permit(:foo, :bar, player_ids: [])
end
end

Rails- How can I create view for nasted tables

I want to get data like this in the show.html.erb , but it doesn't work.
How can I get data from spot table?
here is the code.
show.html.erb
<% #planaction.each do |action| %>
<hr>
<%= action.spot.name %>
<%= action.spot.description %>
<hr>
<%= action.title %>
<%= action.experience %>
<% end %>
plan.rb
class Plan < ActiveRecord::Base
has_many :plan_actions
end
plan_action.rb
class PlanAction < ActiveRecord::Base
belongs_to :plan
has_one :spot
end
spot.rb
class Spot < ActiveRecord::Base
belongs_to :plan_action
end
plan_actions_controller.erb
class PlanPagesController < ApplicationController
def show
#plan = Plan.find(params[:id])
#planaction = #plan.plan_actions
end
end
and error message here
undefined method `name' for nil:NilClass
and here is migration file for spot table.
class CreateSpots < ActiveRecord::Migration
def change
create_table :spots do |t|
t.integer :spot_id
t.integer :plan_action_id
t.string :name
t.text :description
t.time :time_open
t.time :time_close
t.date :dayoff
t.string :address
t.integer :tel
t.string :image
t.string :image2
t.string :image3
t.timestamps null: false
end
end
end
Looks good to me.
The issue is probably (can't be certain without seeing your logs) that the plan_action doesn't have an associated spot record.
To fix this, you should use some conditional logic:
<% #planaction.each do |action| %>
<hr>
<% if action.spot %>
<%= action.spot.name %>
<%= action.spot.description %>
<hr>
<% end %>
<%= action.title %>
<%= action.experience %>
<% end %>
Again, this is speculation. I wrote the answer because I felt it best to provide some sort of idea as to how to resolve it. The above should work.
I also think as Rich Peck that you don't have a record in spots table with plan_action_id corresponding to a plan action.
Following rails convention, I suggest the following:
class PlanAction < ActiveRecord::Base
belongs_to :plan
has_one :spot
delegate :name, :description, to: :spot, prefix: true, allow_nil: true
end
and in your view:
<%= action.spot_name %>
<%= action.spot_description %>
Finally, get your validations corrected. For example, if a plan_action should have a spot, then you need to use nested forms for both spot and plan action.

How to show results based on sql result data

I'm using Closure tree idea in my new website.
In one of the show views I want to select data by the id (1-level descendant) or if the id is null the first level.
How do I connect sql with set result?
The Query:
select id,name
from tags t
join tag_hierarchies th (t.id = th.ancestor_id)
where t.id=nvl(?,0) and th.generations=1
Code so far(problem on app/views/show.erb):
db/schema.rb:
create_table "tags" do |t|
t.string "name", :null=>false
t.boolean "isCat", :default => true
end
create_table "tag_hierarchies", :id => false do |t|
t.integer "ancestor_id", :null => true
t.integer "descendant_id", :null => false
t.integer "generations", :null => false
end
add_foreign_key(:tag_hierarchies, :tags, :column => 'ancestor_id')
add_foreign_key(:tag_hierarchies, :tags, :column => 'descendant_id')
app/models/tag.rb
class Tag < ActiveRecord::Base
#attr_accessible :name, :isCat
validates :name, uniqueness: false, allow_blank: false
end
app/models/Tag_Hierarchie.rb
class TagHierarchie < ActiveRecord::Base
#attr_accessible :ancestor_id, :descendant_id, :generations
end
app/views/show.erb
<% provide(:title, category_name_or_constant(#tags)) %>
<h1><%= category_name_or_constant(#tags)%></h1>
<div class="row">
<div class="span6 offset3">
<%= for(<<here goes the sql by the Closure tree >>) do |f| %>
<%= link_to tag.name, tag %>
<% end %>
</div>
</div>
Add static method (or scope, if you prefer) to your Tag model:
app/models/tag_hierarchy.rb
class TagHierarchy
belongs_to :tag, foreign_key: :ancestor_id
end
app/models/tag.rb
class Tag
has_many :tag_hierarchies, foreign_key: :ancestor_id
def self.descendants(id = nil)
id ||= 0
self.where(id: id).joins(:tag_hierarchies).where(tag_hierarchies: {generations: 1})
end
end
Create a controller:
rake g controller TagsController
Add code to your controller:
app/controllers/tags_controller.rb
class TagsController < ApplicationController
def index
#descendants = Tag.descendants
end
def show
#descendants = Tag.descendants(params[:id])
end
end
Then use all the stuff in your views:
app/views/tags/show.html.erb:
<div class="row">
<div class="span6 offset3">
<%= #descendants.each do |tag| %>
<%= link_to tag.name, tag %>
<%# you can also use tag.tag_hierarchies here %>
<% end %>
</div>
</div>
I suggest you read some tutorials and/or docs on Rails:
Active Record Associations
Rails Routing from the Outside In
Getting Started with Rails

Active record "date" and "nationality" function giving me error. (Undefined method)

I am trying to create a profile page where I can input "born_on". It will be using the
class CreateWineMakers < ActiveRecord::Migration
def change
create_table :wine_makers do |t|
t.string :name
t.date :born_on
t.text :nationality
t.text :profile
t.text :wine
t.integer :wine_list_id
t.timestamps
end
add_index :wine_makers, :wine_list_id
end
end
Here is my view file.
<%= simple_form_for WineMaker.new do |f| %>
<%= f.input :name %>
<%= f.input :profile %>
<%= f.input :wine %>
<%= f.input :born_on %>
<br/>
<%= f.submit "Create", :class => 'btn btn-primary' %>
<% end %>
The "born_on" is giving me error saying the method is not defined. I am confused since all other inputs are working except "born_on" and "nationality". Before, my "born_on" was named "birth_date", and I thought the naming convention was wrong and changed it to "born_on". Here is the controller.
class WineMakersController < ApplicationController
def new
#wine_maker = WineMaker.new
end
def create
#wine_maker = WineMaker.create(wine_maker_params)
redirect_to wine_list_path(#wine_list)
end
def show
end
private
def wine_maker_params
params.require(:wine_maker).permit(:name, :born_on, :nationality, :profile, :wine )
end
end
This seems like such an easy question that I couldn't find similar problems..
Thank you.

How do I create a select box using two tables with a relationship?

I'm trying to create a select box that shows all my ejecutive_name and last_name from my table Ejecutives in my Policies view,but i need to create a search button to get parameters from Ejecutives that i selected
My models have a relationship:
class Policy < ActiveRecord::Base
unloadable
belongs_to :ejecutive
has_many :policy
end
class Ejecutive < ActiveRecord::Base
has_many :policies
end
My tables have a relationship by ejecutive_id:
class CreateEjecutives < ActiveRecord::Migration
def self.up
create_table :ejecutives do |t|
t.string :name,:null=>false
t.string :lastname1,:null=>false
t.timestamps
end
end
def self.down
drop_table :ejecutives
end
end
class CreatePolicies < ActiveRecord::Migration
def self.up
create_table :policies do |t|
t.string :num_policy, :null=>false
t.integer :ejecutive_id
t.timestamps
end
end
def self.down
drop_table :policies
end
end
This is my controller:
class PolicyManagement::PolicyController < ApplicationController
#ejecutives = Ejecutive.find(:all)
#policies = Policy.find(:all)
end
This is my view:
Select Ejecutive:
%= select_tag 'ejecutives',"<option value=\"\">Seleccione</option>"+options_for_select(#ejecutives.collect {|t| [t.name.to_s+" "+t.lastname1.to_s,t.id]})%>
Results
<% #policies.each do |policy| %>
<p> <%= policy.num_policy%> </p>
<p> <%= policy.ejecutive.name %> </p>
<p> <%= policy.ejecutive.last_name %> </p>
<% end %>
I tried this
<% form_tag :controller=>"policy_management/policy",:action =>"generate_print_ejecutive_comercial", :method => 'get' do %>
<p>
<%= text_field_tag :search,params[:search] %>
<%= select_tag "Ejecutives", options_from_collection_for_select(#ejecutives, "id", "name") %>
#Here i in select_tag "ejecutives" need to add searh params..
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
I'm using Rails 2.3.5.
Does somebody know about this problem? I'd really appreciate help.
If i understand correctly, you want the policies for a selected ejecutive, you can do this by saying Ejecutive.find().policies. If want a search button, put your select box in a form tag and post it. In the controller action, you will get the selected id, with which you can execute the line i mentioned above.Hope this helps.

Resources