Jump to content

The ultimate community for Ruby on Rails developers.


Photo

How do you display the characteristics of associated records?


  • Please log in to reply
20 replies to this topic

#1 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 16 August 2013 - 07:01 PM

I have created a many-to-many association and the association model:

class Employee <ActiveRecord

  has_many :Favorites

  has_many :Projects, :through =>Favorites

.....

class Project <ActiveRecord

  has_many :Favorites

  has_many :Employees, :through =>Favorites

............

class Favorite <ActiveRecord

  belongs_to :Employee

  belongs_to :Project

............

 

I have looked up rails associations (a lot) but can find nothing about how to display them.

How do I list the projects that an employee is assigned to?

For example, @employee.Favorites.find(:all) will create an array of the project id's that are the favorites of the given employee, but I can't figure out the code to get to the descriptions of the projects an employee works on.

 

Thanks in advance for any help --Fred


  • james likes this

#2 Jemagee

Jemagee

    Inspector

  • Members
  • 61 posts

Posted 16 August 2013 - 07:12 PM

I'm a realtive newbie and maybe I understand these things completely wrong, but since you used the has many through you can say

projects = @employee.projects

  • james likes this

#3 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 16 August 2013 - 07:31 PM

Jemagee has that spot on but your associations are incorectly defined so for that to work you need to not use class names for your associations

 

So change

class Employee <ActiveRecord
  has_many :Favorites
  has_many :Projects, :through =>Favorites
.....
class Project <ActiveRecord
  has_many :Favorites
  has_many :Employees, :through =>Favorites
............
class Favorite <ActiveRecord
  belongs_to :Employee
  belongs_to :Project

To

class Employee <ActiveRecord
  has_many :favorites
  has_many :projects, :through => favorites
.....
class Project <ActiveRecord
  has_many :favorites
  has_many :employees, :through => favorites
............
class Favorite <ActiveRecord
  belongs_to :employee
  belongs_to :project

Then insted of

 

For example, @employee.Favorites.find(:all)

 

 

You can simply do

 

 

@employee.favorites

The only reason to add a finder on to an association is to filter the associated records further

@employee.favorites.where.not(sacked: true)

Anyway, make those changes and Jemagees' solution

 

I'm a realtive newbie and maybe I understand these things completely wrong, but since you used the has many through you can say projects = @employee.projects

 

Will work for you


  • amite likes this

Programming is just about problem solving!


#4 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 16 August 2013 - 11:12 PM

p.s., Welcome back frederated, I think our paths crossed a few times on the old forum, or am I mistaken?


Programming is just about problem solving!


#5 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 20 August 2013 - 08:34 PM

Indeed, James, you were kind enough to answer a few questions that I had.  I think your icon is a little wierd, though.



#6 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 20 August 2013 - 09:28 PM

Indeed, James, you were kind enough to answer a few questions that I had.  I think your icon is a little wierd, though.

 

Glad to see you back. My avater is better when it's animated. It reflects how I feel most of the time. An alien sitting in the dark typing away :)


Programming is just about problem solving!


#7 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 22 August 2013 - 07:39 PM

Back to work, sigh.
So, changing all the upper case model references to lower case eliminated the immediate errors and made things happen, but I am still left with my original problem: how to list, in a view, the projects an employee works on. in this case, list the project numbers.

Based on other code seen elsewhere, my best guess, in a view, is:

 

early in the view I have:
<%= form_for(@employee) do |f| %>
..... stuff here ......
then, to list an employees projects by project number:

 

<ul>
<%= f.fields_for :Favorite do |pf| %>
  <%= pf.li :"#{Favorite.Project.number}" %>
<% end %>
</ul>

This results in the error:
undefined method 'Project' for #<Class:0x00000.....>

So I still can't figure out how to list the project numbers in the employees favorites list.

 

ANy ideas greatfully entertained and attempted.

-Fred



#8 Vell

Vell

    Dispatcher

  • Members
  • 42 posts
  • LocationWashington, DC

Posted 23 August 2013 - 03:55 AM

Frederated, 

 

I think part of the issue is your still camel casing things when you shouldn't. Your specifying model relations in your view should be an exact match of what you have in your model For example:

 

If you have:

has_many :projects : through => :favorites

 in your employee class then in your view it you should use lower case. 'Projects' is different from 'projects'

 

So in your view try: 

favorite.project.number

Same thing in your fields_for. You probably want to use :favorite instead of :Favorite. I don't know if you will run into issues using a camel case there but I think its good practice if nothing else to use lower case.

 

Also I am also thinking instead of using :Favorites, wouldn't you want to use @employee.favorites in your field_for? I may be wrong about that but somehow it makes sense to me.

 

Happy hunting :)


  • james likes this

#9 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 23 August 2013 - 03:50 PM

When I lower case 'favorite' in the code:
<%= f.fields_for :favorite do |pf| %>
I get the error 'undefined local variable or method 'favorite' for ....

When 'Favorite' is in upper case, as in:
<%= f.fields_for :Favorite do |pf| %>
<%= pf.li :"#{Favorite.project.number}" %>
then the error moves to the second line and is:
undefined method 'project' for ...

This tells me that upper case 'Favorite' is correct because it no longer throws an error, and moves to the next line, where in 'Favorite.project' 'project' is now the error.  Both 'project' and 'Project' give the error.

Using @employee.favorites in the fields_for line gives the error:
undefined method 'favorites' for :@employee:Symbol

One thing that bothers me is, I have the following code in my employees controller:
   @employee = Employee.find_by_number(params[:id])
   @favorites = @employee.Favorite.find(:all)
   @my_project = @favorites.Projects.number
So @favorites, loaded as the employee favorites, has a method 'Projects' when executed in the employees controller, but when executed in the view I get the error 'undefined method 'project' or 'undefined method 'projects'' or 'undefined method 'Projects' or 'undefined method 'Project', all possible combinations of pluralization and capitalization fail.

I am totally flumoxed by ror and can make no sense of how it works.



#10 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 23 August 2013 - 03:58 PM

Are you trying to list all the favourites for an employee?

If so you need

 

<%= f.fields_for :@employerr.favorites do |pf| %>
  <%= pf.li :"#{f.object.project.number} unless f.object.project.blank?" %>
<% end %>

 

 

f.object is the current favourite instance in the fields_for loop

 

The above assumes that you have a belongs_to :project in the Favourite class and the

f.object.project.blank?

prevents null pointer exceptions if the favourite does not have a project instant to get the number for


Programming is just about problem solving!


#11 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 23 August 2013 - 05:18 PM

Thanks for your reply.
belongs_to :project is in the Favorite class as described above in an earlier post.

f.object.project.number resulted in the error "undefined method 'project' for #<Employee:0x000..."
On the other hand, pf.object.project.number worked.

 

However, attempting to list the favorites using the following code:
<ul>
<%= f.fields_for @employee.favorites do |pf| %>
<li><%= "#{pf.object.project.number}" %></li>
<% end %>
</ul>

lists only 1 project, but my projects list has 15 projects.
Making progress slowly.



#12 Vell

Vell

    Dispatcher

  • Members
  • 42 posts
  • LocationWashington, DC

Posted 23 August 2013 - 05:53 PM

Federated,

 

I think also 

<li><%= "#{pf.object.project.number}" %></li>

probably doesn't need to be interpolated. I you can just use

<li><%= pf.object.project.number %></li>

and that should give you the same result once things are all fixed.

 

I think at this point, you may want to post your entire view along with your class associations in a single post so we can get an idea of what this particular part of your application looks like.

 

Another thought occurs to me. Have you used

 <%= debug @employee.favorits%>

to see what your employee object is showing as the associated favorites? This might give you some insight also into what might be going wrong.



#13 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 23 August 2013 - 08:22 PM

<% debug @employee.favorites %>

This lists the 15 association records 'owned' by the employee under consideration,

i.e., it lists the id, employee_id, project_id, created_at and updated_at data for this employee, from the join table.

 

I will work on this some more on Monday, but isn't there some standard way to reach through the join table into the project table to get the charcteristics of the projects associated with this employee?  That should be a bread-and-butter standard, rails is making me want to revert to SQL with which I can grab the data in about 5 seconds, instead of working days to figure out how rails wants to do this.



#14 Vell

Vell

    Dispatcher

  • Members
  • 42 posts
  • LocationWashington, DC

Posted 23 August 2013 - 09:14 PM

The only other thing I can point out from you code is the beginning f in your f.fields_for line. In the example of fields_for that I see, they aren't using anything like that.

 

Example:

<%= fields_for @person.permission do |permission_fields| %>
Admin?: <%= permission_fields.check_box :admin %>
<% end %>

Thats the only thing I can think of that may be causing your issue.



#15 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 26 August 2013 - 04:29 PM

f.fields_for @employee.favorites  results in only the last project number to list
fields_for @employee.favorites    results in "undefined method 'model_name' for Array:Class
fields_for :Favorites    results in "undefined method 'project' on the pf.object.project line
f.fields_for :Favorites  also results in "undefined method 'project' on the pf.object.project line
 

The 'f' is defined at the top of my form:

<%= form_for(@employee) do |f| %>

 

I have seen numerous examples of fields_for as simply fields_for, and also as f.fields_for, but I have never seen an explanation of the difference.

 

The lines

<div>

<%= debug @employees.favorites

</div>

This gives a complete list of the 15 join records which join the employee to their projects.

However, for reasons I do not understand, the fields_for section never iterates through the project list, it just displays 1 project.

 

Personally, I can hardly imagine a non-trivial database model without join tables, and yet here a I am in Rails struggling mightly to simply display the data related by a join table.  This makes no sense to me.

 

Thanks for any help. -Fred



#16 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 27 August 2013 - 01:27 AM

I have seen numerous examples of fields_for as simply fields_for, and also as f.fields_for, but I have never seen an explanation of the difference.

 

 

It takes a while to grasp what forms are all about but hopefully this will give you a eureka moment as it really is very very simple and it's all about arranging the url and the params that get sent back to the server.

 

The form_for takes an object of type ActiveRecord. When you don;t have an ActiveRecord then use form_tag.

Rails checks the state of the AR object to see if it is a new record. If it is a new record it will arrange for the the form to send an HTTP PUT request and if it's an update it will be arranged for a PATCH request (as of Rails 4 PATCH is replacing POST) and the model's class name is used to determine which controller will be used.

It's not really magic but it looks like it's magic and it sure save one heck of a lot of coding :)

 

I think I got that the right way round, I always get muddled up over PUT, PATCH and POST and which is for updates and which is for creates.

Read more here http://weblog.rubyon...od-for-updates/

 

Right then Rails eventually receives the request from the browser and decides which action to use as determined by your routes.rb. If using a RESTfull route (i.e. resources :some_controller) Rails will automatically send to the right action be it update or create.

 

Now having got that out of the way the rest of the form declaration is about arranging the params hash that goes along with the request.

 

If you have

<%= form_for(@employee) do |f| %>
  <%= f.text_field :name %>

Anything that is attached to the f object (e.g. f.name) will be inside the employee key in the params hash with it's own key (in this case "name" that the controllers action receives.

So your params hash for the above example might look like this

{emplyee: => {name: => "fred"}}

So you can see that the name is inside the params for the employee

 

Remove the f. from in front of the text_field for the name in the template like so

<%= form_for(@employee) do |f| %>
<%= text_field :name %>

And your params would look something like this

{emplyee: => {}, name: => "fred"}

 

And then if you attach a fields_for

<%= form_for(@employee) do |f| %>
  <%= f.fields_for @employee.favourites do |xyz| %>

Then you will nest the favourites attributes inside the employee hash that is passed back to the action

 

Thus you are using nested params and youi will need to tell your model to expect nested params when you pass the params hash to the create or update method on the model class

Employee.create(params[:employee])

or the new syntax style for Rails 4

Employee.create(employee_params)

Actually haven't got my head round how the new syntax actually works yet

 

Anyway, hope you get the idea.

 

The best way you can check to see what is going on is by looking at the source code of your page in the browser and looking at the params that are passed back to the controller's action in the log files.


Programming is just about problem solving!


#17 fredrated

fredrated

    Signalman

  • Members
  • 11 posts

Posted 03 September 2013 - 06:44 PM

In the end, I am still back at the original problem: I cannot display the characteristics of the projects that are related to a given employee.

An employee has projects related to them through a join table (and model) 'favorites'.  At this (early) stage in my software project, I would simply like to display the projects an employee works on, project number and description, as in:

302002 Earthquake Prepardness

406023 Local Housing Needs

503402 Disaster Recovery

 

What I have tried:

In the Employees controller I populate the @favorites variable with:
@favorites = @employee.favorites
Then in the view I have tried (to just get project numbers):

<div>
<ul>
<%= @favorites.each { |m| "<li> #{m.project.number}</li><br />" } %>
</ul>
</div>

Oddly, this results in a list of the join records: id, employee_id, project_id, created_at etc. for each join record, but not the actual information I want: the project number of the related project.  This is in spite of the fact that if I add to the view the code:
<%= @favorites[0].project.number %>
I get the project number of the first project the employee works on!  Why does the project number list in this line of code referencing an actual favorites record, but the project number does not list within the iterator? (This is a retorical question, I don't really care, I just want my projects to list!)

I have also tried:

<div>
<ul>
<%= f.fields_for @favorites do |p| %>
  <li><%= p.object.project.number %></li>
<% end %>
</ul>
</div>

This code results in only the employees 1st project number listing, even though the employee has 15 records in the favorites table.  I cannot get the code to iterate through the list of related projects.

Any help to actually list the projects an employee works on greatly appreciated!



#18 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 03 September 2013 - 08:23 PM

<div>
<ul>
<%= @favorites.each { |m| "<li> #{m.project.number}</li><br />" } %>
</ul>
</div>

Oddly, this results in a list of the join records: id, employee_id, project_id, created_at etc. for each join record, but not the actual information I want: the project number of the related project.  This is in spite of the fact that if I add to the view the code:

 

That's because <%= @favorites.each { |m| "<li> #{m.project.number}</li><br />" } %> is exactly what you are telling it to do.

You are saying for each favourite record display the favourite records attributes and inject the project number this is because of the <%=

 

I think you meant to do

<ul>
<% @favorites.each do |m| %>
  <li>
     <%= m.project.number %>
  </li>
<%end%>
</ul>

The above assumes that a favourite belongs_to project rather than has_many projects


Programming is just about problem solving!


#19 Rowel

Rowel

    Controller

  • Members
  • 109 posts

Posted 03 September 2013 - 11:04 PM

I'm head-scratching at a similar problem... I'm just working at the Rails console but I see the same problem.

 

When used in an iterator, the info from the parent database doesn't display.  

But when I try to access them individually (by record id, first, last, etc), it works.  

 

This task is so much easier and a no brainer using actual raw SQL code. But Rails in trying to simplify everything for everybody and hide the implementation details just made it difficult and exasperating. 



#20 james

james

    Guard

  • Moderators
  • 221 posts
  • LocationLeeds, U.K.

Posted 03 September 2013 - 11:47 PM

When used in an iterator, the info from the parent database doesn't display.

 

What is the problem?

 

It shouldn't be any more difficult than

>> @recs = SomeModel.all
>> @recs.each do |rec|
>> puts rec.some_attribute
>> puts rec.some_parent.attribute
>> end

Programming is just about problem solving!





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users