I'm new to rails and I have been experimenting. I have to tables companies and contacts and i have set up the associations and relationship. what I would like to do is this.
Currently, in the contacts table it brings back a number for the value of the company name. What I would like instead is the the value of the company name instead of the number. I have tried changing it from the company_id to company_name - but I then get an error about no method being found. Can anyone help on how I do this.
You can delegate to the company
class Contact < ActiveRecord::Base
delegate :name, to: :company, prefix: true
end
<%= #contact.company_name %>
I assume a contact will always belong to a company, if not add, allow_nil: true
If you're looping through them like this:
<% #contacts.each do |contact| %>
<tr>
<td><%= #contact.company_name %></td>
</tr>
<% end %>
I doubt you've defined #contact as you're looping through #contacts calling them contact you want
<% #contacts.each do |contact| %>
<tr>
<td><%= contact.company_name %></td>
</tr>
<% end %>
I.e. without the # - you're using a local variable not an instance variable.
Related
Filter with another table's attribute (one to many) in Rails
I'm a beginner on rails and currently working on my class project. I have a lead and a lead_comments table, lead has_many lead_comments and lead_comment belong_to lead, which I got those established.
In Model:
class Lead < ApplicationRecord
has_many :lead_comments
end
class LeadComment < ApplicationRecord
belongs_to :lead
end
In my lead's index view, I am trying to setup a date filter base on the Update_at in the lead_comments, which is the attribute from another table:
<div class="date-search">
<%= form_tag leads_path, method: :get do %>
<%= date_field_tag 'date_search[date_from]', #date_search.date_from %>
<%= date_field_tag 'date_search[date_to]', #date_search.date_to %>
<%= submit_tag 'Date Search' %>
<% end %>
</div>
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Phone Number</th>
<th>Last Updated Date</th>
<th colspan="3">Action</th>
</tr>
</thead>
<tbody>
<% #leads.each do |lead| %>
<tr>
<td><%= lead.lead_firstname %></td>
<td><%= lead.lead_lastname %></td>
<td><%= lead.lead_phone %></td>
<td><%= lead.lead_comments.maximum(:updated_at) %></td>
<td><%= link_to 'Detail', lead %></td>
<td><%= link_to 'Edit/Update', edit_lead_path(lead) %></td>
<td><%= link_to 'Remove', lead, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
In my lead controller, I set up another controller for lead search purpose:
def index
#date_search = LeadSearch.new(params[:date_search])
#leads = #date_search.scope
end
In my lead search model, I believe where my problem is:
class LeadSearch
attr_reader :date_from, :date_to
def initialize(params)
params ||= {}
#date_from = parsed_date(params[:date_from], 30.days.ago.to_date.to_s)
#date_to = parsed_date(params[:date_to], Date.today.to_s)
end
def scope
Lead.lead_comments.maximum(:updated_at).where('updated_at BETWEEN ? AND ?', #date_from, #date_to)
end
private
def parsed_date(date_string, default)
Date.parse(date_string)
rescue ArgumentError, TypeError
default
end
end
My lead_comment table only has one attributes, which is comment, along with the two automated date attributes created by Rails, created_at and updated_at.
Whenever I run the application I get an error that tells me undefined method "lead_comments". I thought I have already established the has_many and belong_to relationship and this still not working. Can someone please help me out and guide me to the right direction? Thank you for your time.
you are doing Lead.lead_comments which is wrong. You are using the class name instead of the object. There should be a Lead model's object.
#lead.lead_comments
First, try your query in rails console. then use it in the controller for better perspective. try following,
Lead.lead_comments # will give error
instead use,
Lead.last.lead_comments # success
Issues in your code:
Lead.lead_comments
Let's simplify this: think about lead_comments as a method that given a lead returns its comments. But we need a lead. That's why called on Lead does not work. That would work called on a Lead object.
Lead.lead_comments.maximum(:updated_at)
Here you're composing a SQL query though ActiveRecord but calling maximum stop this composition because that require a value so you're not able to call anything else after methods like maximum
Let's make your code work
def scope
Lead.where('updated_at BETWEEN ? AND ?', #date_from, #date_to)
end
in your view
<td><%= lead.lead_comments.maximum(:updated_at) %></td>
Note here that this code has always been correct because you call lead_comments on a lead object.
I know, this performs one query for each Lead object but to make it works with just one query looks like another question
Hey I'm new to Rails and all this so bear with me, thanks!
I have two models:
class User < ApplicationRecord
has_and_belongs_to_many :sports
end
class Sport < ApplicationRecord
has_and_belongs_to_many :users
end
My users have a few different sports that they can choose each. I'm simply trying to display all users in a table, along with which sports they do. However.. the only way I've managed to get anything without an error is by using current_user as shown below. I've been looking how to do this for hours... I know it's going to be stupidly simple but I just can't figure it out or even know how to go in the right direction.
# users_controller.rb
def index
#users = User.all
#sports = current_user.sports
end
# users/index.html.erb
<% #users.each do |user| %>
<tr>
<td><%= link_to user.name, user %></td>
<td><%= link_to user.email, user %></td>
<% #sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
</tr>
<% end %>
That's my current code but obviously this shows only the signed in users associations and repeats it for the other users like this:
<table>
<tr>
<th>Name</th>
<th>Sport 1:</th>
<th>2:</th>
</tr>
<tr>
<td>User 1 (current_user)</td>
<td>Football</td>
<td>Running</td>
</tr>
<tr>
<td>User 2</td>
<td>Football (User 1's Sports)</td>
<td>Running </td>
</tr>
</table>
Thanks in advance.
You can try using the following and deleting #sports = current_user.sports:
<% user.sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
using user.sports while looping through each of the user will lead to N+1 queries on your database. You can change your controller method to something like
def index
#users = User.all.eager_load(:sports)
end
and then in html
<% user.sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
This will load users along with left_outer_join on sports table and this will save to lot of extra queries on your database.
For Info you can refer this good blog.
Thanks
I am new to Rails and I am struggling on something which sounds easy but can not get it to work. I have two models Students and Attendances.
Student model:
name lastname classroom_id
Attendance model:
present:boolean absent:boolean halfday:boolean attnd_date:date student_id
Students has_many :attendances and attendance belongs_to :student.
I can make an entry for individual student and take their attendance however I want to generate a view where I show all the students (or show all students for a given classroom) and next to each student name I would like to show the three checkboxes so that I can mark who is present and absent in one go rather than one by one and submit the form.
Any help here is much appreciated. Using Rails 4 and ruby 2.2.0
Thanks
You can make an edit action, where you will find the classroom for which you want to mark attendances.
class AttendancesController < ApplicationController
def edit
#classroom = Classroom.find(<classroom-id>)
end
def update
end
end
In your view edit.html.erb
<%= form_for(#classroom, url: '/attendances/:id', method: :put) do |f| %>
<table>
<%- #classroom.students.each do |student| %>
<tr>
<td><%= student.name %></td>
<td><%= checkbox_tag "attendances[#{student.id}][present]" %></td>
<td><%= checkbox_tag "attendances[#{student.id}][absent]" %></td>
<td><%= checkbox_tag "attendances[#{student.id}][halfday]" %></td>
</tr>
<% end %>
</table>
<%= f.submit %>
<% end %>
This way, when you submit the form, you will receive these params in your update action:
`{ attendances: { '1' => { present: false, absent: true, halfday: false }, '2' => { present: true, absent: false, halfday: false }, ... } }`.
Then you can write logic in your action to save these details to database.
Note: This is kind of pseudo code. Please check the syntax and options for different html tags.
Thanks to #Jagdeep Singh for getting me up and running. I have now made the process more simple so I can get my head around. I just want to get the list of all students and create their attendances.
My view:
<% #students = Student.all %>
<%= form_for(:attendances, url: '/admin/attendances/') do |f| %>
<table>
<%= #today %>
<th>Name</th><th>Present</th><th>Absent</th><th>halfday</th>
<%- #students.each do |student| %>
<tr>
<td><%= student.first_name %></td>
<td><%= check_box_tag "attendances[#{student.id}][present]" %></td>
<td><%= check_box_tag "attendances[#{student.id}][absent]" %></td>
<td><%= check_box_tag "attendances[#{student.id}][halfday]" %></td>
</tr>
<% end %>
</table>
<%= f.submit %>
<% end %>
when I click on create attendance button it just creates just one with record with all default params and.
I am sorry I am still learning but once I get my head around on how I can create attendances for all 10 students i have in one go.
Basically I have a table output on a page, base view. The headers (milestones) are dynamically integrated by passing the Milestones class and iterating through each with an .each do. The table rows are then initially generated by doing the same thing, pulling in client, trade, and units, into the first three columns. The rest of the columns should be dates (class entry attribute due) that are calculated by a lookup of sorts using the client and trade attributes accessible by the loop, and the milestone ID accessible by an array created when dynamically generating them for the headers. This all works correctly as it should, and the table generates fine.. however the dates are output as #<Entry:0x000000056faa90> and so on.
Now I have come across this problem before, and basically the fix was to add a class definition and tell it to return what I assume to be the attribute of that instance. For example:
class Client < ActiveRecord::Base
validates :id, presence:true
validates :client, presence:true
def name
return self.client
end
end
And this works great, but only for how I access the client. Unfortunately I am accessing entry.due differently. Here are my relevant bits of code.
base_controller.rb
class BaseController < ApplicationController
def index
#trades = Trade.all
#milestones = Milestone.all
##entries = Entry.all #Didn't seem relevant to how I am trying to access the information.
end
end
entry.rb
class Entry < ActiveRecord::Base
belongs_to :client
belongs_to :trade
belongs_to :milestone
validates :client, presence:true
validates :trade, presence:true
validates :milestone, presence:true
validates :due, presence:true
# Some of my trial and error; all to no avail.
def due
return self.due
end
def self.due
return self.due
end
def pickDue(c,t,m)
ret = self.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", c, t, m).first
return ret
end
end
base > index.html.erb
<table>
<thead>
<tr class="sort">
<th>Client</th>
<th>Trade</th>
<th>Units</th>
<%
ms = []
#milestones.each do |milestone|
ms.push(milestone)
%>
<th><%= milestone.name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% #trades.order("entered").last(10).each do |trade| %>
<tr>
<td><%= trade.client.name %></td>
<td><%= trade.name %></td>
<td><%= trade.units %></td>
<% ms.each do |msr| %>
<td>
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%#= Entry.pickDue(trade.client, trade, msr) %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
With this approach, I can load the page error-free, but instead of showing dates, I am shown objects like #<Entry:0x000000056faa90>.
If I add .due to the end of the selector:
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first.due %>
# undefined method `due' for nil:NilClass
If I add .due after anything else:
<%= Entry.due.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%= Entry.select("date_format(due, '%m/%d/%Y')").due.where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).first %>
<%= Entry.select("date_format(due, '%m/%d/%Y')").where("client_id=? AND trade_id=? AND milestone_id=?", trade.client, trade, msr).due.first %>
# stack level too deep
I have tried dozens of different methods acquired through several days of looking into this with no luck. Besides the current situation where it outputs objects instead of the object attribute, the next closest I've come I think is when I got an error saying it could not find the due method for an ActiveRecord object, but I don't remember how got to that error.
I would really appreciate any input.
If I understand correctly, I would get rid of that trial and error code in your model, and then in your view you can just call this:
Entry.where(client: trade.client, milestone: msr, trade: trade).pluck(:due)
This goes inside your milestone loop in your view, like this:
<% ms.each do |msr| %>
<td>
<%= Entry.where(client: trade.client, milestone: msr, trade: trade).pluck(:due).first %>
</td>
<% end %>
Pluck returns an array of values from the columns you specify (see the relevant section of the RailsGuide, so note that the .first method is the Array#first method, not the ActiveQuery#first method. You could even make a query that passes in all of the milestones at once instead of running a query for each milestone, thereby preventing an N+1 query situation.
That would be:
<% Entry.where(client: trade.client, trade: trade, milestone: ms).pluck(:due).each do |entry_due_date| %>
<td>
<%= entry_due_date %>
</td>
<% end %>
Lastly, while I gave you the code for the SQL query in the view, it is generally considered a bad practice in Rails to write SQL queries in your view, and you should probably make this into a method in your Entry model.
I think you might be looking for attributes method.
entry = Entry.new
entry.attributes #=> returns a hash of key-value attribute pairs
I have developed rails app with three classes.
class Location < ActiveRecord::Base
has_many :observations
end
class Observation < ActiveRecord::Base
belongs_to :location
has_one :rainfall
has_one :temperature
has_one :winddirection
has_one :windspeed
has_one :dewpoint
end
class Rainfall < ActiveRecord::Base
belongs_to :observation
end
Railfall is related to Observation through an observation_id and Observation is related to Location through a location_id.
If i go to console and type something like:
Location.all.first.observations.first.rainfall.value
it returns me the data in value column of rainfall
However when i want to combine all the info in rails in a table and in my view/location/index.html.erb i put:
<tbody>
<% #locations.each do |location| %>
<tr>
<td><%= location.name %></td>
<% unless #observations = nil %>
<% location.observations.each do |obs| %>
<td><%= obs.name %></td>
<% unless #rainfalls = nil %>
<td><%= obs.rainfalls.value %></td>
<% end %>
<% end %>
I get an error:
undefined method `rainfalls' for Observation:0x00000004219068>
I have spent the last 5 hours trying just about everything i can and am getting a tad frustrated....any ideas?
Note i have a locations controller but observation and rainfall were generated as models, so do not. Am thinking i need to add something to my location controller, just can't work out what, and I am unsure why it returns the correct data in console, just no in app.
Observation.has_one :rainfall, so it should be:
<%= obs.rainfall.value %>
You have one to one relationship, not has_many. Replace
<%= obs.rainfalls.value %>
by
<%= obs.rainfall.value %>