Get data from related tables, rails - ruby-on-rails

some fields tables City
create_table "cities", force: :cascade do |t|
t.string "name"
some fields tables Order
create_table "orders", force: :cascade do |t|
t.integer "count"
t.integer "first_city_id"
t.integer "last_city_id"
model Order
class Order < ActiveRecord::Base
belongs_to :city, inverse_of: :city
end
model City
class City < ActiveRecord::Base
has_many :orders, inverse_of: :order
end
working output data from Order in view
<% #order.each do |orders| %>
<p> <%= orders.count %> </p>
<% end %>
Method in controller
def edit
#orders= Order.all
#order=Order.where(:user_id == current_user)
end
How to get the value from the name Sity table if first_city_id or last_city_id in the Order table contains id required entry of City Table?

You should setup the order associations as follows:
class Order < ActiveRecord::Base
belongs_to :first_city, class_name: "City" # it would use first_city_id as the foreign key by default to access city table
belongs_to :last_city, class_name: "City"
end
Getting the first/last city name from an order becomes easy:
#order = Order.first
#order.first_city.name
#order.last_city.name
If you want to get the first/last city name from an order list, you should use includes or joins to avoid the n+1 query:
#orders = Order.includes(:first_city, :last_city)
<% #orders.each do |order| %>
<p> First City: <%= order.first_city.name %> </p>
<p> Last City: <%= order.last_city.name %> </p>
<% end %>
or
#orders = Order.
joins("LEFT JOIN cities AS first_cities ON first_cities.id = orders.first_city_id").
joins("LEFT JOIN cities AS last_cities ON last_cities.id = orders.last_city_id").
select("orders.*, first_cities.name AS first_city_name, last_cities.name AS last_city_name")
<% #orders.each do |order| %>
<p> First City: <%= order.first_city_name %> </p>
<p> Last City: <%= order.last_city_name %> </p>
<% end %>
You can get more information and helps from these two docs "RailsGuides - Active Record Associations" and "RailsGuides - Active Record Query Interface".

Related

Rails - filtering multiple tables with scope

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 %>

How to query another tables when Loop a records collection?

I want to loop a collection of records, and check the answers status from another tables. The detail describle in the View part.
Model
# Quiz
class Quiz < ApplicationRecord
has_many :quiz_questions
has_many :questions, :through => :quiz_questions
has_many :quiz_details
end
# QuizDetail
class QuizDetail < ApplicationRecord
belongs_to :quiz
end
# QuizQuestion
class QuizQuestion < ApplicationRecord
belongs_to :quiz
belongs_to :question
end
Controller
# quiz_questions_controller.rb
def index
#questions = #quiz.questions
# #quiz_details = #quiz.quiz_details
end
DB
create_table "quiz_details", force: :cascade do |t|
t.integer "quiz_id"
t.integer "question_id"
t.integer "user_id"
t.boolean "is_correct", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
View
<ul>
<% #questions.each do |question| %>
<li>
# I want to check the answers status
# The status in the quiz_details table
# `is_correct` : true or false
<%= link_to question.title, quiz_question_path(#quiz, question) %>
</li>
<% end %>
</ul>
I want to know if there is a way to do this clearly?
I think you need to add some more associations to make this work smoothly. If I am reading your database table correctly, quiz_details also belongs to questions. You can add that association as follows:
Add to your Models
# Question
class Question < ApplicationRecord
has_many :quiz_details do
def by_quiz(quiz)
where(quiz_id: quiz.id)
end
end
end
# QuizDetail
class QuizDetail < ApplicationRecord
belongs_to :quiz
belongs_to :question
end
In your Controller
def index
# If you iterate over questions use this:
#questions = #quiz.questions.includes(:quiz_details)
# If you iterate over quiz_details use this:
#quiz_details = #quiz.quiz_details.includes(:question)
end
In your View
If you definitely need to iterate over questions:
<ul>
<% #questions.each do |question| %>
<li>
<%= question.quiz_details(#quiz).first.is_correct %>
<%= link_to question.title, quiz_question_path(#quiz, question) %>
</li>
<% end %>
</ul>
However, this will likely generate a nasty N+1 query that is inefficient. You can check your console to see if it does.
A better approach is to iterate over quiz_details if possible:
<ul>
<% #quiz_details.each do |detail| %>
<li>
<%= detail.is_correct %>
<%= link_to detail.question.title, quiz_question_path(#quiz, detail.question) %>
</li>
<% end %>
</ul>

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.

Rails 4: child record ID is saved but not its attributes, using nested form with has_many through relationship

I have 3 models with a has_many through relationship: Food (eg: Chocolate), Sub (Chocolate food substitute), Joint (joint table).
Say #food = Food.find(1); The has_many through relationship allows me to do #subs = #food.subs which return all substitutes associated with #food. This work fine, however only the Sub id is saved and not its attributes which are :name and :description as you can see it returned nil when trying to save #food.subs in my create action in my controller:
=> #<ActiveRecord::Associations::CollectionProxy [#<Sub id: 28,name:nil,description:nil,created_at:
"2015-01-07 00:40:35", updated_at: "2015-01-07 00:40:35">]>
I guess the issue lies with my create action in my food controller and perhaps something to do with my nested form as well. I spent countless hours trying to figure this out I am so desperate to find an answer. I really do not know where to look anymore.
I am new to rails so thanks a lot for your help and your time, I really appreciate it. Please if possible adapt your answer to my beginner level :-) .
Down below are samples of my controller, form and relevant information.
Here are my models:
class Food < ActiveRecord::Base
has_many :joints
has_many :subs, :through => :joints
accepts_nested_attributes_for :subs
end
class Sub < ActiveRecord::Base
has_many :joints
has_many :foods, :through => :joints
accepts_nested_attributes_for :foods
end
class Joint < ActiveRecord::Base
belongs_to :food
belongs_to :sub
end
Here is my db-schema FYI:
create_table "foods", force: true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "joints", force: true do |t|
t.integer "food_id"
t.integer "sub_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "subs", force: true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Here is my foods_controller:
def new
#food = Food.new
#sub = Sub.new
end
def create
#food = Food.new(food_params)
#food.subs.build(params[:subs])
#food.save
respond_to do |format|
if #food.save
format.html { redirect_to #food, notice: 'Food was successfully created.' }
format.json { render :show, status: :created, location: #food }
else
format.html { render :new }
format.json { render json: #food.errors, status: :unprocessable_entity }
end
end
end
private
def food_params
params.require(:food).permit(:name, :description, subs_attributes: [:name, :description])
end
end
Here is my views/foods/_form:
<%= form_for(#food) do |f| %>
<% if #food.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#food.errors.count, "error") %> prohibited this food from being saved:</h2>
<ul>
<% #food.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div>
<%= f.fields_for(#sub) do |sub| %>
<div class="field">
<%= sub.label :name %>
<%= sub.text_field :name %>
</div>
<div class="field">
<%= sub.label :description %>
<%= sub.text_area :description %>
</div>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My routes in case it helps:
resources :foods
resources :subs
resources :joints
root "foods#index"
Thank you very much !
Antoine.
In your new action:
def new
#food = Food.new
#food.subs.build
end
and in your view:
<%= f.fields_for :subs do |sub| %>
When you're passing directly an object, this object becomes the new form_builder's object - rails have no idea it is in any way connected with original object so it will result in different field names.
When you pass a symbol, rails will first try to find if your current object defines subs_attributes method. If so it will loop over subs association and build the fields for each associated model.
Reference here.
UPDATE - answer to comment:
Firstly - #subs is not a symbol, it is an instance variable. Symbols start with a colon like :subs. When fields_for receives an argument, it checks whether it is a symbol or object. In former case it search an object associated with form builder (f.object) to find out if it defines <passed_symbol>_attributes=. That way it knows that the model accepts nested attributes for this association so it can behave accordingly (the new form builder is created for each associated object with a correct name - <symbol>_attributes).
When object is passed, rails has no way of detecting if this is in ay way connected to the current object - you could have two associations for the same type of objects, or even it might have absolutely nothing to do with the original object. In that case fields_for acts like it was a nested form_for - resulting form builder will carry the model name of the object (f.object.class.model_name.singular)

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