Simple Nested Attribute Association issue Ruby on Rails - ruby-on-rails

I have two models, a User model and a Hair Model. I have created a form that successfully allows a user to submit and save their name, location in the user model and hair color in the hair model.
What I can't seem to work out is how to retrieve the user's hair color in my views?
Hair is currently a nested attribute of User in my routes and the current result I am being shown by the view is Rosie Ealing #<Hair:0x4d5e078>
Any help that can be offered really would be much appreciated.
User Model
class User < ActiveRecord::Base
has_one :hair, :dependent => :destroy
accepts_nested_attributes_for :hair
attr_accessible :location, :name, :hair_attributes
end
Hair Model
class Hair < ActiveRecord::Base
belongs_to :user
attr_accessible :color, :user_id
end
User Index
<% #user.each do |user| %>
<%= user.name %>
<%= user.location %>
<%= user.hair %>
<% end %>
User Model Migration
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :location
t.timestamps
end
end
end
Hair Model Migration
class CreateHairs < ActiveRecord::Migration
def change
create_table :hairs do |t|
t.string :color
t.integer :user_id
t.timestamps
end
end
end
User Controller
class UsersController < ApplicationController
def new
#user = User.new
#user.build_hair
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render "user/new"
end
end
def index
#user = User.all
end
end

Try this:
<% #user.each do |user| %>
<%= user.name %>
<%= user.location %>
<%= user.hair.color %>
<% end %>
Your code is return a link to the object Hair, rather the the data contained in it.

Related

issue and error message : Couldn't find School with 'id'=

What I want to achieve is that list each member with schools and thereafter list Schools with all belonging members. Schools can have many members and members can have many schools also. I have the following set up in the system, but have problems finding the solution for it. Here it is how my code looks like:
controller:
class MembersController < ActionController::Base
before_action :set_school
def index
#members = Member.all
end
def new
#member = Member.new
end
def create
#member = Member.new(member_params)
#member.school = #school
#member.save
redirect_to members_path
end
private
def set_school
#school = School.find(params[:school])
end
def member_params
params.require(:member).permit(:name, :email,:school)
end
end
This is my route:
Rails.application.routes.draw do
get 'schools/index'
resources :members
resources :school
end
My view looks like:
<% #members.each do |member| %>
<%= member.name %>
<%= member.email %>
<%= member.school %>
<% end %>
model for members:
class CreateMembers < ActiveRecord::Migration[5.0]
def change
create_table :members do |t|
t.string :name
t.string :email
t.timestamps
end
end
my School model :
class CreateSchools < ActiveRecord::Migration[5.0]
def change
create_table :schools do |t|
t.string :name
t.timestamps
end
and the reference:
class AddSchoolRefToMembers < ActiveRecord::Migration[5.0]
def change
add_reference :members, :school, foreign_key: true
end
Any help would be great! Thank you!
You have set before_action for all actions in controller. For, index and new there is no school_id, so you have to run before_action only for create.
Change below code
before_action :set_school
to
before_action :set_school, only: ['create']
<%= simple_form_for [#member] do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.collection_select :school_id, School.all, :id, :name %>
<%= f.submit %>
<% end %>
Change the strong params
def member_params
params.require(:member).permit(:name, :email, :school_id)
end
And you can remove the before_action :set_school
Also you need to change the action as school_id is already in params
def create
#member = Member.create(member_params)
redirect_to members_path
end
#imocsari to get the name of school change your view like
<% #members.each do |member| %>
<%= member.name %>
<%= member.email %>
<%= member.school.try(:name) %>
<% end %>
member.school will give you associated school of member with all column, if you want to show name of school this is way member.school.try(:name) or member.school.name
member.school.try(:name) this will return nil if there is not any school for member, It will by pass exception
member.school.name this will raise error if there is not any school for member.

Dealing With Multiple Objects

I am new to Rails and currently trying to add a patient to an existing dentist appointment. I am having difficulty setting up my views and controllers properly. How can I properly accomplish this?
Note: With the way I have set things up, I can create an appointment and tie it to a dentist. Of course, the patient_id is missing.
Models:
class Dentist < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :dentists
belongs_to :patients
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :dentists, :through => :appointments
end
Schema:
ActiveRecord::Schema.define(version: 20151107052115) do
create_table "appointments", force: :cascade do |t|
t.integer "dentist_id"
t.integer "patient_id"
t.datetime "appt_date"
end
create_table "dentists", force: :cascade do |t|
t.string "name"
end
create_table "patients", force: :cascade do |t|
t.string "name"
end
end
Routes:
Rails.application.routes.draw do
concern :commentable do
resources :appointments
end
resources :dentists, concerns: :commentable
resources :patients, concerns: :commentable
end
Dentists Controller:
class DentistsController < ApplicationController
def new
#dentist = Dentist.new
end
def create
#dentist = Dentist.new(dentist_params)
if #dentist.save
redirect_to dentists_path
else
render :new
end
end
...
end
Appointments Controller:
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new(appt_params)
if Appointment.exists?(:appt_date => #appointment.appt_date)
render :new
else
#appointment.save
redirect_to dentist_path(#dentist)
end
end
...
end
Patients Controller:
TBD
Dentists View (Show):
<p><%= #dentist.name %> DDS</p>
<% if #dentist.appointments.any? %>
<% #dentist.appointments.each do |appt| %>
<ul>
<li><%= appt.appt_date %></li>
<p><%= link_to "Edit", edit_dentist_appointment_path(#dentist, appt) %> |
<%= link_to 'Delete', dentist_appointment_path(#dentist, appt), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %> |
<%= link_to 'Add Patient', new_patient_path %></p>
</ul>
<% end %>
<% else %>
<p>There are currently no appointments scheduled</p>
<% end %>
<p><%= link_to 'Delete Dentist', dentist_path(#dentist), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %></p>
<p><%= link_to 'Create an appointment', new_dentist_appointment_path(#dentist) %></p>
<p><%= link_to 'Return to list', root_path %></p>
I am new to Rails
Welcome!
You need to change your belongs_to references to be singular:
class Appointment < ActiveRecord::Base
belongs_to :dentist
belongs_to :patient
end
--
Because I can't see where you're trying to achieve this functionality, I'll show you what I'd do (using the appointment#edit action):
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def edit
#appointment = Appointment.find params[:id]
end
def update
#appointment = Appointment.find params[:id]
#appointment.save appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/edit.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
--
If you're trying to set the patient from your appointments#create method, you'll be best doing this:
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/new.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
I think what you are asking, is can you create an appointment through both the Dentist model and the Patient model at the same time eg. #dentist.#patient.apointment.new
you cannot do that. Based on the relationships you have set up, you will either want to create the appt from the Dentist, like you have now, and pass in the patient ID as an attribute, or vice-versa. OR, create through your Appointment model eg. Appointment.new(dentist: #dentist, patient: #patient, ...)

RAILS 4 - How to show NAME instead of ID in index view?

rails newbie here.
I want my questions/view/index to show the name of the language associated with a question, rather than the language_id.
My question model is:
class Question < ActiveRecord::Base
validates :phrase, presence: true
has_many :answers, dependent: :delete_all
belongs_to :language
end
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.string :phrase
t.string :language
t.timestamps
end
end
end
class AddLanguageIdToQuestions < ActiveRecord::Migration
def change
add_column :questions, :language_id, :integer
end
end
My language model is:
class Language < ActiveRecord::Base
end
class CreateLanguages < ActiveRecord::Migration
def change
create_table :languages do |t|
t.string :name
t.timestamps
end
end
end
In my questions controller:
def index
#questions = Question.all
#language = Language.find(#questions.language_id)
end
In the questions/_form.html.erb:
<p>
<%= f.label :language_id %><br>
<%= f.select :language_id, #languages.map { |l| [l.name, l.id] }, {:prompt => 'select language'} %>
</p>
And in the questions/view/index.html.erb:
<% #questions.each do |question| %>
<li>"<%= link_to question.phrase, question %>" in <%= question.language.name %>?%></li>
<% end %>
The error I keep getting, despite trying several variations of "question.language.name" (which works just fine in the show view) is "undefined method "language_id" in the index view.
Any help would be very much appreciated.
just change your index action to:
def index
#questions = Question.all.includes(:language)
end
edit
<% #questions.each do |question| %>
<% unless question.language.nil? %>
<li>"<%= link_to question.phrase, question %>" in <%= question.language.name %>?%></li> <% end %>
<% end %>
OR
<% #questions.each do |question| %>
<li>"<%= link_to question.phrase, question %>" in <%= question.language.name unless question.language.nil? %>?%></li>
<% end %>
Both will work fine depends on you what do you want.
feel free to ask if problem continues or not solved.
Your Problem
What you were doing wrong was:
finding all questions and then this line was wrong finding all question's language at once.
#language = Language.find(#questions.language_id)
And to avoid this: better solution is to avoid N + 1 query problem using includes
ActiveRecord Associations
This sounds like a job for ActiveRecord Associations, specifically the has_many association:
ActiveRecord associations basically use a foreign_key in your database to pull relational data & append to your object. Currently, you're only focused on using a single object, without any associated data.
--
Your problem can be fixed using the following:
#app/models/question.rb
Class Question < ActiveRecord::Base
belongs_to :language
end
#app/models/language.rb
Class Language < ActiveRecord::Base
has_many :questions
end
This will allow you to call the following in your controller & view:
#app/controllers/questions_controller.rb
Class QuestionsController < ApplicationController
def index
#questions = Question.all
end
end
#app/views/questions/index.html.erb
<% #questions.each do |question| %>
<%= question.language.name %>
<% end %>
--
Bonus
You can use the .delegate method to provide you with the ability to stop the law of dementer issue:
#app/models/question.rb
Class Question < ActiveRecord::Base
belongs_to :language
delegate :name, to: :language, prefix: true #-> #question.language_name
end
#questions.language_id is incorrect because you are trying to retrieve a single id from an array. i.e. Question.all returns an array. Also, Language.find requires a single id parameter to return a single language.
What exactly are you trying to return with Language.find(#questions.language_id) ? An array of languages that have a question that belong to it?
Also, where is the _form.html.erb partial called? By default, rails scaffold will call the partial in the new and edit actions and therefore if you are trying to set #languages for the select field in the form partial, then you would not do it in the index action. Also, Consider using a collection select for this field as it is for an association.

Comments on multiple models

Within my rails app, I currently have comments setup to work with my posts model, which is functioning properly. How do I add comments to my books model?
Here is what I have so far:
Here is what I have in my schema for the comments:
create_table "comments", force: true do |t|
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "post_id"
t.integer "book_id"
end
In my user model:
class User < ActiveRecord::Base
has_many :comments
acts_as_voter
end
In my post model:
class Post < ActiveRecord::Base
has_many :comments
end
In my book model:
class Book < ActiveRecord::Base
has_many :comments
end
In my comment model:
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :book
belongs_to :user
acts_as_votable
end
In my comments controller:
class CommentsController < ApplicationController
def create
post.comments.create(new_comment_params) do |comment|
comment.user = current_user
end
respond_to do |format|
format.html {redirect_to post_path(post)}
end
end
def upvote
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to #post}
end
end
private
def new_comment_params
params.require(:comment).permit(:body)
end
def post
#post = Post.find(params[:post_id])
end
end
In my routes file:
resources :posts do
resources :comments do
member do
put "like", to: "comments#upvote"
end
end
end
In my view:
<% #post.comments.each do |comment| %>
<%= comment.body %>
<% if user_signed_in? && (current_user != comment.user) && !(current_user.voted_for? comment) %>
<%= link_to “up vote”, like_post_comment_path(#post, comment), method: :put %>
<%= comment.votes.size %>
<% else %>
<%= comment.votes.size %></a>
<% end %>
<% end %>
<br />
<%= form_for([#post, #post.comments.build]) do |f| %>
<p><%= f.text_area :body, :cols => "80", :rows => "10" %></p>
<p><%= f.submit “comment” %></p>
<% end %>
What do I add to my comments controller to get comments working on both posts and books? What do I add to my routes file?
Thanks in advance for any help.
You don't want to specify each type of object that can hold Comment objects. That creates a headache of if-elsif-else blocks all over the place. Instead, you want things to be Commentable, and they all will have .comments on them.
This is called a polymorphic association in Active Record. So you would have your models something like:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Book < ActiveRecord::Base
has_many :comments, as: :commentable
end
And modify your database accordingly, it's all in the linked article. Now when you build a Comment object for a form, it will have pre-populated a commentable_id and commentable_type, which you can toss in hidden fields. Now it doesn't matter what the Comment is associated with, you always treat it the same.
I'd leave User as a separate association, since it's not really the same idea.

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