ROR Self referential has_many through with accept_nested_attributes_for - ruby-on-rails

I wondered if someone could take a quick look at this. I'm making a simple conversion application which converts between units of measurement. I need to be able to self reference the table using a join table which stores the relationship between each, along with the conversion between each. This then would be referenced between either side of the relationship. For example 1cm = 10mm and 10mm = 1cm.
So thus far I have this:
#migrations
create_table :measures do |t|
t.string :name
end
create_table :measure_measures do |t|
t.integer :measure_id
t.integer :related_measure_id
t.integer :conversion
end
class Measure < ActiveRecord::Base
has_many :related_measures,
:foreign_key => 'measure_id',
:class_name => 'MeasureMeasure',
:dependent => :destroy
has_many :measures, :through => :related_measures
accepts_nested_attributes_for :related_measures,
:reject_if => proc { |attrs| attrs['related_measure_id'].blank? ||
attrs['quantity'].blank? },
:allow_destroy => true
end
#controller
#measure = Measure.find params[:id
#form
<% form_for #measure do |f| %>
<% fields_for :related_measures do |f_r_m| %>
<%= f_r_m.text_field :related_measure_id -%>
<%= f_r_m.text_field :quantity -%>
<% end %>
<% end %>
For the most part this works ok. Except I cannot access the name of the related measure, only the owner.
I need to get it somehow like this:
f_r_m.object.related_measure.name
but clearly despite my best efforts i cannot set it up and receive the error.
undefined method `owner_measure' for #<MeasureMeasure:0x1053139a8>
Help would be very much appreciated. :)

At first glance the problem comes from the has many through definition. By failing to define the foreign key, Rails, assumes measure_id in the join table. Which would just link back to the measure you're trying to find it from.
has_many :measures, :through => :related_measures, :foreign_key => :related_measure_id
We can't diagnose this error without seeing the innards of the join model.
f_r_m.object.related_measure refers to join table.
But I suspect it's because you haven't defined the relationship properly in the join model. Which should look something like this:
class MeasureMeasures < ActiveRecord::Base
belongs_to :measure
belongs_to :related_measure, :classname => "Measure"
end

Related

has_and_belongs_to_many relationship in ruby on rails

I am trying to make a website for a tennis coach using ruby on rails. I am completely new to this and I am struggling with some of the terminology. The website has a login system where users can sign in and then sign up for different events that the tennis coach has created. So basically the users can go to many events and the events can have many users attending. I have made a has_and_belongs_to_many relationship between my users table and events table and the code is below:
Here is my migration:
class CreateEventsUsers < ActiveRecord::Migration
def change
create_table :events_users, :id => false do |t|
t.integer :event_id
t.integer :user_id
end
add_index :events_users, :event_id
add_index :events_users, :user_id
add_index :events_users, [:event_id, :user_id]
end
def self.down
drop_table :events_users
end
end
Here are my models:
class User < ActiveRecord::Base
has_and_belongs_to_many :events
end
class Event < ActiveRecord::Base
has_and_belongs_to_many :users
end
Here is my event form:
<li>
<%= link_to event.title, event %>
| <%= link_to "delete", event, method: :delete %>
| *insert sign up here*
</li>
Basically my question is, how would I make a form and a controller that would let a signed in user, sign up for one of the events in the database? I have been stuck on this for days. Any help would be greatly appreciated.
I would suggest using a has_many through instead of using has_and_belongs_to for scalability issues and is a suggested practice. Coming to your requirement, I did a similar thing but with users following other users and tried to adopt some of them for your requirement.
class Event < ActiveRecord::Base
has_many : Relationship
has_many : users, :through => :Relationships
end
class User < ActiveRecord::Base
has_many :Relationship
has_many :events, :through => :Relationships
//define other methods//
def follow!(event_id)
relationships.create!(event_id: event.id)
end
end
class Relationship < ActiveRecord::Base
attr_accessible :event_id
belongs_to :events
belongs_to :users
end
You need to create the relationship migration like this
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :user_id
t.integer :event_id
t.timestamps
end
add_index :relationships, :user_id
add_index :relationships, :event_id
add_index :relationships, [:user_id, :event_id], unique: true
end
end
You can try adding a button on the events page view like this.
<%= form_for(current_user.relationships.build(evnt_id: #user.id)) do |f| %>
<div><%= f.hidden_field :event_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
Relationship controller can be something like this.
class RelationshipsController < ApplicationController
def create
#user = User.find(params[:relationship][:event_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
end
end
end
This tutorial was very helpful for me in developing the application for users to follow other users. Hope it will help you too. Good luck !!
http://ruby.railstutorial.org/chapters/following-users#top
If you haven't discovered it already, you are in for a real treat if you checkout railscasts from Ryan Bates. Here is a tutorial on using checkboxes in a form with a HABTM association--actually he suggests, and you should consider it, moving to has_many, through relationship instead of HABTM.
Regardless, it should at least give you an idea of how you can make it work in your app. http://railscasts.com/episodes/17-habtm-checkboxes-revised. That episode may require a membership, however, the $9 bux/month is totally reasonable for what you're getting.

associations without the user_id column

I created a FriendRequest.rb model in my Rails app with the following table columns.
create_table "friend_requests", force: true do |t|
t.integer "requesting_user_id"
t.integer "requested_friend_id"
t.datetime "created_at"
t.datetime "updated_at"
end
With relationships defined as you see below, I added this code to a /views/users/show.html.erb page show the friend requests that have been made for each user. However, I'm getting this error
PG::UndefinedColumn: ERROR: column friend_requests.user_id does not exist
because, I obviously didn't create a user_id column. Is there a way that I can make this code work by adding more information to the relationships? or should I scrap my work and do it differently?
<% for user in #user.friend_requests %>
<li><%= h user.name %></li>
<%= link_to "Add Friend", friend_requests_path(:friend_id => user), :method => :post %>
<% end %>
User.rb
has_many :friend_requests
FriendRequest.rb
belongs_to :user
Just change your has_many association for:
has_many :friend_requests, foreign_key: 'requesting_user_id'
By default, Rails will look for [model_name]_id in the other table, this is why it is looking for user_id, but by adding the foerign_key: option, you can override this default behaviour and tell Rails what is the name of the foreign_key you want to use.
You can use this configuration:
class User < ActiveRecord::Base
has_many :friend_requests
has_many :requesters, through: friend_requests
end
class FriendRequest < ActiveRecord::Base
belongs_to :requester, foreign_key: 'requesting_user_id'
belongs_to :requested, foreign_key: 'requested_friend_id'
validates :requester_id, presence: true
validates :requested_id, presence: true
end
Take a look at:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Look especially for the options :primary_key and :foreign_key

Combining polymorphic association with many-to-many association... is this setup right?

I have a Genre model, and I want both videos to have many genres and profiles to have many genres. I also want genres to have many videos and genres to have many profiles. I understand the polymorphic and join table stuff, so I'm wondering if my code below will work as I intend it to. Also, I'd appreciate any advice on how to access things in my controller and views.
This is what I envision that the join table should look like (I don't think I need an elaborate :has :through association because all I need in the join table are the associations and nothing else, so the table won't have a model):
genres_videos_profiles:
-----------------------------------------------------
id | genre_id | genre_element_id | genre_element_type
Here's my genre.rb:
has_and_belongs_to_many :genre_element, :polymorphic => true
Here's video.rb:
has_and_belongs_to_many :genres, :as => :genre_element
Here's profile.rb:
has_and_belongs_to_many :genres, :as => :genre_element
Will this work as I intend it to? I'd like some feedback.
As far as I know HABTM associations can´t be polymorphic, I couldn´t find an example like yours in the API documentation. If you want only join tables, your code could look like this:
class Genre
has_and_belongs_to_many :videos
has_and_belongs_to_many :profiles
end
class Video
has_and_belongs_to_many :genres
end
class Profile
has_and_belongs_to_many :genres
end
And access it like Mike already wrote:
#genre.profiles
#profile.genres
#genre.videos
#video.genres
Migrations (for join tables only):
class CreateGenresVideosJoinTable < ActiveRecord::Migration
def self.up
create_table :genres_videos, {:id => false, :force => true} do |t|
t.integer :genre_id
t.integer :video_id
t.timestamps
end
end
def self.down
drop_table :genres_videos
end
end
class CreateGenresProfilesJoinTable < ActiveRecord::Migration
def self.up
create_table :genres_profiles, {:id => false, :force => true} do |t|
t.integer :genre_id
t.integer :profile_id
t.timestamps
end
end
def self.down
drop_table :genres_profiles
end
end
I think that has_and_belongs_to_many can be a bit difficult to follow when it comes to polymorphic (if it even works). So if you want to do the polymorhpic thing, then you can't use any "through" syntax:
class Genre < ActiveRecord::Base
has_many :genres_videos_profiles
end
class GenresVideosProfile
belongs_to :genre
belongs_to :genre_element, :polymorphic => true
scope :videos, where(:genre_element_type => "Video")
scope :profiles, where(:genre_element_type => "Profile")
end
And then you use it like:
# All genre elements
#genre.genres_videos_profiles.each do |gvp|
puts gvp.genre_element.inspect
end
# Only video genre elements
#genre.genres_videos_profiles.videos.each do |gvp|
puts gvp.genre_element.inspect
end
Check out that: http://blog.hasmanythrough.com/2006/4/3/polymorphic-through
For me it was perfect and clean!

In Ruby on Rails, how do I create a table/model that has a field in it that references the same table?

I am trying to do this in Rails 3. I create a table (syntax on code examples may not be exactly right, I am trying to recreate from memory):
create_table "persons", :force => true do |t|
t.string "name"
t.integer "guest_of_id"
end
And I want guest_id to reference another row in the persons table. Each person is the guest of only one person. So in the model I set up the association:
class Person < ActiveRecord::Base
belongs_to :GuestOf, :class => "Person", :foreign_key => "guest_of_id"
end
However, when I try to reference the guestOf field
a_person.GuestOf.name
I get the error
undefined method 'eq' for nil:NilClass
Is this possible in Rails? Am I doing something wrong? Am I missing a has_many relationship? I strongly suspect my Google-Fu is failing me. The only possible solution I have found is http://railscasts.com/episodes/163-self-referential-association but he is establishing a many to many relationship and I think it is more complicated than what I am trying to do.
Thanks.
You really should be able to just do:
class Person < ActiveRecord::Base
belongs_to :host, :class => "Person", :foreign_key => "guest_of_id"
has_one :guest, :class => "Person", :foreign_key => "guest_of_id"
end

eager loading with attributes in join model rails

My app uses a :has_many :through association, and I'm having trouble figuring out how to most efficiently load and display data from both ends of the association and the association itself.
Here are my classes:
class Person < ActiveRecord::Base
has_many :people_ranks
has_many :ranks, :through => :people_ranks
has_many :institutions_people
has_many :institutions, :through => :institutions_people
belongs_to :school
belongs_to :department
end
class Institution < ActiveRecord::Base
has_many :institutions_people
has_many :people, :through => :institutions_people
end
class InstitutionsPerson < ActiveRecord::Base
belongs_to :institution
belongs_to :person
end
and their corresponding models:
create_table :people, :force => true do |t|
t.string :name
t.string :degree
t.integer :year_grad
t.integer :year_hired
end
create_table :institutions, :force => true do |t|
t.string :name
t.integer :ischool
end
create_table :institutions_people, :id => false do |t|
t.integer :institution_id
t.integer :person_id
t.string :rel_type
end
I want to show a person's institution info with something like #person.year_hired, #person.institution.name, and #person.institution.institutions_people.rel_type (where rel_type is either "graduated" or "hired:), but I know that third part won't work. Using the following in the show bit in the person_controller:
#person = Person.find(params[:id], :include => [:school, :department, :institutions_people, :people_ranks, {:institutions_people => :institution}, {:people_ranks => :rank}])
gives me access to #person.institutions and #person.institutions_people, but how do I connect the rel_type attribute from the join to the person-institution relationship? (I'm coming from PHP and now how to build the SQL and loop through it there, but RoR has me stumped.)
I've looked for help under "eager loading" and "associations with :has_many :through", but I get answers about building the associations. My question is really about accessing the association's data after it exists. My app uses static data, and I'm not worried about the update, destroy, or create methods. Thank you for your help!
The way to access the data is through the institutions_people association. So, you would do something like:
me = Person.first
rel = me.institutions_people.first
And then in the view
<%= rel.rel_type %> from <%= rel.institution.name %>
Alternatively, you can give yourself a full list of institutions along with their info:
me = Person.first
And then in the view:
<% for ip in me.institutions_people %>
<%= ip.rel_type %> from <%= ip.institution.name %>
<% end %>

Resources