has_many through relationship to the same Model - ruby-on-rails

I have User table:
class User < ActiveRecord::Base
has_many :broker_clients, :class_name => "BrokerClients", :foreign_key => "broker_id"
has_many :clients, :through => :broker_clients, :foreign_key => "broker_id"
has_many :brokers, :through => :broker_clients, :foreign_key => "client_id"
end
And BrokerClients table:
class BrokerClients < ActiveRecord::Base
belongs_to :broker, class_name: "User"
belongs_to :client, class_name: "User"
end
Now when I create a relationship:
>> BrokerClients.create(broker_id: User.first.id, client_id: User.last.id)
User Load (9.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
User Load (1.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
(1.3ms) BEGIN
SQL (41.5ms) INSERT INTO "broker_clients" ("broker_id", "client_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["broker_id", 4], ["client_id", 210], ["created_at", Fri, 10 Oct 2014 13:43:27 EDT -04:00], ["updated_at", Fri, 10 Oct 2014 13:43:27 EDT -04:00]]
(0.5ms) COMMIT
=> #<BrokerClients id: 1, broker_id: 4, client_id: 210, created_at: "2014-10-10 17:43:27", updated_at: "2014-10-10 17:43:27">
>> User.first.brokers.first
When I try to get clients it's working fine:
>> User.first.clients.first
User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
User Load (1.2ms) SELECT "users".* FROM "users" INNER JOIN "broker_clients" ON "users"."id" = "broker_clients"."
=> #<User id: 210, ....
But for the client when I try to get the brokers related to it wont work:
>> User.last.brokers.first
User Load (0.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
User Load (0.7ms) SELECT "users".* FROM "users" INNER JOIN "broker_clients" ON "users"."id" = "broker_clients"."broker_id" WHERE "broker_clients"."broker_id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["broker_id", 210]]
=> nil
Any help ?

Change in the User modal as:--
has_many :broker_clients, :class_name => "BrokerClients", :foreign_key => "broker_id"
has_many :clients, :through => :broker_clients, :foreign_key => "broker_id"
has_many :inverse_broker_clients, :class_name => "BrokerClients", :foreign_key => "client_id"
has_many :brokers, :through => :inverse_broker_clients, :foreign_key => "client_id"

Look at your insert vs. your query:
Insert:
SQL (41.5ms) INSERT INTO "broker_clients" ("broker_id", "client_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["broker_id", 4], ["client_id", 210], ["created_at", Fri, 10 Oct 2014 13:43:27 EDT -04:00], ["updated_at", Fri, 10 Oct 2014 13:43:27 EDT -04:00]]
Queries:
User Load (0.7ms) SELECT "users".* FROM "users" INNER JOIN "broker_clients" ON "users"."id" = "broker_clients"."broker_id" WHERE "broker_clients"."broker_id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["broker_id", 210]]
You insert (broker_id=4, client_id=210) but you query for broker_id=210. That's why you're getting nil for brokers.first. Pretty sure you're foreign keys are backwards:
has_many :clients, :through => :broker_clients, :foreign_key => "broker_id"
has_many :brokers, :through => :broker_clients, :foreign_key => "client_id"
Should be
has_many :clients, :through => :broker_clients, :foreign_key => "client_id"
has_many :brokers, :through => :broker_clients, :foreign_key => "broker_id"
Hope that helps.

Related

Rails: How to call one-to-one relationship in rails

I'm new to rails and I want to know how to fetch a one-to-one relationship. I want to fetch users city. In my postgresql database I have:
cities Table:
city:varchar
zipcode: integer
users Table
name:varchar
city_id:int
and in city and user model I have:
class City < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :city
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I tried the following in my search controller but didnt work, when logged in:
current_user.city
I get the following error
Processing by SearchController#index as HTML
Parameters: {"utf8"=>"✓", "q"=>"", "criteria"=>"1", "commit"=>"Search"}
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1
PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1
Completed 500 Internal Server Error in 11ms
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1):
why am I suppose to add a user_id column to cities table, when I have cities foreign key in users table? I dont want to add user_id into cities table.
You can use has_one :through association with join table. Some example for you below.
user model:
class User < ActiveRecord::Base
has_one :city, through: :user_city
has_one :user_city
end
city model:
class City < ActiveRecord::Base
belongs_to :user
end
user city join model:
class UserCity < ActiveRecord::Base
belongs_to :city
belongs_to :user
end
migration for join tables:
class JoinUserCity < ActiveRecord::Migration
def change
create_table :user_cities do |t|
t.integer :user_id
t.integer :city_id
end
end
end
Test in rails console:
=> u = User.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:14.595728"], ["updated_at", "2014-12-07 15:47:14.595728"]]
(3.3ms) commit transaction
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> nil
=> c = City.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "cities" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:24.535039"], ["updated_at", "2014-12-07 15:47:24.535039"]]
(3.3ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):004:0> u.city = c
UserCity Load (0.3ms) SELECT "user_cities".* FROM "user_cities" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "user_cities" ("city_id", "user_id") VALUES (?, ?) [["city_id", 1], ["user_id", 4]]
(1.0ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):005:0> u.save
(0.1ms) begin transaction
(0.1ms) commit transaction
=> true
=> u = User.last
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
take a look at the document of has_one and belogns_to,
belongs_to(name, options = {})
Specifies a one-to-one association with another class. This method should only be used if this class
contains the foreign key. If the other class contains the foreign key, then you should use has_one
instead.
as the user table has the foreign key, you should change your model definition like this
class City < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
belongs_to :city
end

validates_uniqueness_of fails when saving directly on the join model

I have three models, Team, Player and TeamMembership. TeamMembership defines the many-to-many relationship between Team and Player. When creating a new Team, the user gets 11 dropdown menus, each containing all the available players. However, a Player can only have a single membership with a Team, which is what I want to validate.
class Player < ActiveRecord::Base
has_many :team_memberships
has_many :teams, :through => :team_memberships
end
class Team < ActiveRecord::Base
has_many :team_memberships
has_many :players, :through => :team_memberships
accepts_nested_attributes_for :players, :team_memberships
validates_associated :team_memberships
end
class TeamMembership < ActiveRecord::Base
belongs_to :team
belongs_to :player
validates_uniqueness_of :player_id, scope: :team_id
end
# GET /teams/new
def new
#team = Team.new
11.times { #team.team_memberships.build }
end
<%= f.fields_for :team_memberships do |team_memberships_form| %>
<%= team_memberships_form.label :player_id %>
<%= team_memberships_form.select(:player_id, options_from_collection_for_select(#Player.available, :id, :name)) %>
<br />
<% end %>
When trying to create a new team, the following appears in the dev log. (Edited down to only 3 players for brevity)
Started POST "/teams" for 127.0.0.1 at 2014-02-06 10:48:40 +0100
Processing by FantasyTeamsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"OwP+nfksvaD0WTQdGjqF5p/shzkiaAodigbFTC6PDD0=", "team"=>{"name"=>"asd", "tournament_id"=>"1", "team_memberships_attributes"=>{"0"=>{"player_id"=>"12"}, "1"=>{"player_id"=>"12"}, "2"=>{"player_id"=>"12"}, "3"=>{"player_id"=>"12"}}}, "commit"=>"Create Fantasy team"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Role Exists (0.3ms) SELECT 1 AS one FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND "roles"."name" = 'admin' LIMIT 1 [["user_id", 1]](0.3ms) BEGIN
FantasyTeamMembership Exists (0.5ms) SELECT 1 AS one FROM "team_memberships" WHERE ("team_memberships"."player_id" = 12 AND "team_memberships"."team_id" IS NULL) LIMIT 1
CACHE (0.0ms) SELECT 1 AS one FROM "team_memberships" WHERE ("team_memberships"."player_id" = 12 AND "team_memberships"."team_id" IS NULL) LIMIT 1
CACHE (0.0ms) SELECT 1 AS one FROM "team_memberships" WHERE ("team_memberships"."player_id" = 12 AND "team_memberships"."team_id" IS NULL) LIMIT 1
CACHE (0.0ms) SELECT 1 AS one FROM "team_memberships" WHERE ("team_memberships"."player_id" = 12 AND "team_memberships"."team_id" IS NULL) LIMIT 1
SQL (0.5ms) INSERT INTO "teams" ("created_at", "name", "tournament_id", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Thu, 06 Feb 2014 09:48:40 UTC +00:00], ["name", "asd"], ["tournament_id", 1], ["updated_at", Thu, 06 Feb 2014 09:48:40 UTC +00:00], ["user_id", 1]]
SQL (0.4ms) INSERT INTO "team_memberships" ("team_id", "player_id") VALUES ($1, $2) RETURNING "id" [["team_id", 8], ["player_id", 12]]
SQL (0.3ms) INSERT INTO "team_memberships" ("team_id", "player_id") VALUES ($1, $2) RETURNING "id" [["team_id", 8], ["player_id", 12]]
SQL (0.3ms) INSERT INTO "team_memberships" ("team_id", "player_id") VALUES ($1, $2) RETURNING "id" [["team_id", 8], ["player_id", 12]]
(14.7ms) COMMIT
Redirected to http://lolhost:3000/teams/8
Completed 302 Found in 111ms (ActiveRecord: 34.3ms)
As can be seen, because of the way I save the records through accepts_nested_attributes_for, validation does not reflect the actual SQL which will run.
How do I get the behaviour that I want?
Worked around this by adding a custom validation method to the Team model.
validate :unique_players
def unique_players
player_ids = team_memberships.map { |ft| ft.player_id }
if player_ids != player_ids.uniq
errors.add(:team_memberships, "must have unique players.")
end
end

Losing an Attribute When Saving Through an Association w/ Scope (Rails 4.0.0)

The Code (Rails 4.0.0)
class Track < ActiveRecord::Base
has_many :artist_tracks
has_many :owning_artists,
-> { where(:artist_tracks => { :artistic_role_id => 1 }) },
:through => :artist_tracks,
:source => :artist
end
class ArtistTrack < ActiveRecord::Base
belongs_to :artist
belongs_to :track
belongs_to :artistic_role
end
class Artist < ActiveRecord::Base
has_many :artist_tracks
has_many :tracks, :through => :artist_tracks
end
Finding Works
# artist_tracks.artistic_role_id is properly set to "1"
2.0.0p195 :003 > Track.last.owning_artists
Track Load (1.1ms) SELECT "tracks".* FROM "tracks" ORDER BY "tracks"."id" DESC LIMIT 1
Artist Load (0.8ms) SELECT "artists".* FROM "artists" INNER JOIN "artist_tracks" ON "artists"."id" = "artist_tracks"."artist_id" WHERE "artist_tracks"."artistic_role_id" = 1 AND "artist_tracks"."track_id" = $1 [["track_id", 10]]
Create Does Not Work
# artist_tracks.artistic_role_id is totally missing from the INSERT
2.0.0p195 :005 > Track.create!(name: "test_name", lyrics: "test_lyrics", owning_artist_ids: [1])
Artist Load (1.3ms) SELECT "artists".* FROM "artists" WHERE "artists"."id" = $1 LIMIT 1 [["id", 1]]
(0.5ms) BEGIN
Artist Exists (0.7ms) SELECT 1 AS one FROM "artists" WHERE ("artists"."name" = 'TestArtist1' AND "artists"."id" != 1) LIMIT 1
SQL (0.7ms) INSERT INTO "tracks" ("created_at", "lyrics", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Thu, 13 Jun 2013 22:20:14 UTC +00:00], ["lyrics", "test_lyrics"], ["name", "test_name"], ["updated_at", Thu, 13 Jun 2013 22:20:14 UTC +00:00]]
#
# Y U NO have artist_tracks.artistic_role_id?
#
SQL (0.7ms) INSERT INTO "artist_tracks" ("artist_id", "created_at", "track_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["artist_id", 1], ["created_at", Thu, 13 Jun 2013 22:20:14 UTC +00:00], ["track_id", 12], ["updated_at", Thu, 13 Jun 2013 22:20:14 UTC +00:00]]
(1.0ms) COMMIT
According to the Rails Guide for Active Record Associations (4.3.3.1 where), I believe my usage of the scope and expectation are valid:
If you use a hash-style where option, then record creation via this
association will be automatically scoped using the hash.
Why is the artist_tracks.artistic_role_id attribute being lost? If my expectations are wrong, I'd like to understand why and how to implement an alternative solution.
I have also listed this as an issue on the Rails repo. Any insight is appreciated! Thank you
I believe that what is happening is that the associated model actually being created here is the join model, artist_tracks, and not the association with the actual conditions on it. You could probably fix this by declaring an alternate join association with conditions on it, and then attaching owning_artists through that instead. Like this:
class Track < ActiveRecord::Base
has_many :artist_tracks
has_many :owning_artist_tracks,
-> { where(:artistic_role_id => 1) },
:class_name => "ArtistTrack"
has_many :owning_artists,
:through => :owning_artist_tracks,
:source => :artist
end

Rails has_many :through dependent :destroy behaving very oddly

User:
class User < ActiveRecord::Base
attr_accessible :email, :username, :password, :password_confirmation, :remember_me
has_many :tasks_users, :dependent => :destroy, :conditions => {:is_owner => true}
has_many :tasks, :through => :tasks_users, :source => :task
Task:
class Task < ActiveRecord::Base
include RankedModel
ranks :sort_order
acts_as_taggable
has_many :tasks_users
has_many :users, :through => :tasks_users
TasksUser:
class TasksUser < ActiveRecord::Base
attr_accessible :is_owner
belongs_to :user
belongs_to :task
validates_uniqueness_of :user_id, :scope => [:user_id, :task_id]
end
They key here is dependent destroy.
Whenever I try to destroy my user, which should destroy the join model, I end up getting this odd sql error:
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 17]]
(0.1ms) begin transaction
ActsAsTaggableOn::Tagging Load (0.1ms) SELECT "taggings".* FROM "taggings" WHERE "taggings"."tagger_id" = 17 AND "taggings"."tagger_type" = 'User'
TasksUser Load (0.1ms) SELECT "tasks_users".* FROM "tasks_users" WHERE "tasks_users"."user_id" = 17 AND "tasks_users"."is_owner" = 't'
Could not log "sql.active_record" event. NoMethodError: undefined method `name' for nil:NilClass
**SQLite3::SQLException: no such column: tasks_users.: DELETE FROM "tasks_users" WHERE "tasks_users"."" = ?**
(0.1ms) rollback transaction
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: tasks_users.: DELETE FROM "tasks_users" WHERE "tasks_users"."" = ?
from /home/steveq/.rvm/gems/ruby-1.9.3-p194#rails32/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `initialize'
The line with the double asterisk is the line in question - it appears to be searching for a record in "tasks_users"."".
If all I do is change :conditions => {:is_owner => false}, the sql executes without a problem:
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 17]]
(0.1ms) begin transaction
ActsAsTaggableOn::Tagging Load (0.2ms) SELECT "taggings".* FROM "taggings" WHERE "taggings"."tagger_id" = 17 AND "taggings"."tagger_type" = 'User'
TasksUser Load (0.1ms) SELECT "tasks_users".* FROM "tasks_users" WHERE "tasks_users"."user_id" = 17 AND "tasks_users"."is_owner" = 'f'
List Load (0.2ms) SELECT "lists".* FROM "lists" WHERE "lists"."owner_id" = 17
Relationship Load (0.1ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."user_id" = 17
SQL (0.3ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 17]]
(299.4ms) commit transaction
Any ideas about what's happening here, and why having the condition of is_owner change from true to false allows the query and delete statement to execute?
Thanks
Ugh - Hopefully I can save someone else from banging their heads against this wall for an hour.
Problem was how I specified the :conditions.
It needed to be put in double quotes:
has_many :tasks_users, :dependent => :destroy, :conditions => "is_owner = 'true'"
That was it.

Nested associations failing on save

Trying to set up nested associations ( with accepts_nested_attributes_for ) in Rails 3, but save is failing without showing any errors on the model.
db/schema.rb:
create_table "question_responses", :force => true do |t|
t.string "answer"
t.boolean "correct"
t.integer "question_id"
t.integer "quiz_result_id"
end
create_table "quiz_results", :force => true do |t|
t.integer "student_id"
t.integer "quiz_id"
t.text "message"
end
question_response.rb:
class QuestionResponse < ActiveRecord::Base
belongs_to :question
belongs_to :quiz_result, :inverse_of => :question_responses
validates :question, :presence => true
validates :quiz_result, :presence => true
before_save :check_answer
end
quiz_result.rb:
class QuizResult < ActiveRecord::Base
validates :quiz, :presence => true
validates :student, :presence => true
validates :quiz_id, :uniqueness => { :scope => :student_id }
validate :student_teacher
belongs_to :quiz
belongs_to :student
has_many :question_responses, :dependent => :destroy, :inverse_of => :quiz_result
accepts_nested_attributes_for :question_responses
end
Any suggestions for how I can get this working?
server output:
Started POST "/quiz_results" for 127.0.0.1 at 2012-03-20 10:20:24 +0000
Processing by QuizResultsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"YceTVCh/GxdReb3KRMNj+cJhm6k0jwhsHl3LcJlSDJM=", "quiz_result"=>{"student_id"=>"695735877", "quiz_id"=>"17663260", "question_responses_attributes"=>{"0"=>{"question_id"=>"14376743", "answer"=>"test"}}}}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 113461968]]
Student Load (0.2ms) SELECT "students".* FROM "students" WHERE "students"."id" = 695735877 LIMIT 1
Teacher Load (0.4ms) SELECT "teachers".* FROM "teachers" WHERE "teachers"."id" = 657318460 LIMIT 1
(0.1ms) begin transaction
Quiz Load (0.4ms) SELECT "quizzes".* FROM "quizzes" WHERE "quizzes"."id" = 17663260 LIMIT 1
CACHE (0.0ms) SELECT "students".* FROM "students" WHERE "students"."id" = 695735877 LIMIT 1
QuizResult Exists (0.1ms) SELECT 1 FROM "quiz_results" WHERE ("quiz_results"."quiz_id" = 17663260 AND "quiz_results"."student_id" = 695735877) LIMIT 1
CACHE (0.0ms) SELECT "teachers".* FROM "teachers" WHERE "teachers"."id" = 657318460 LIMIT 1
CACHE (0.0ms) SELECT "teachers".* FROM "teachers" WHERE "teachers"."id" = 657318460 LIMIT 1
Question Load (0.1ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = 14376743 LIMIT 1
SQL (0.3ms) INSERT INTO "quiz_results" ("created_at", "message", "quiz_id", "student_id", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Tue, 20 Mar 2012 10:20:24 UTC +00:00], ["message", nil], ["quiz_id", 17663260], ["student_id", 695735877], ["updated_at", Tue, 20 Mar 2012 10:20:24 UTC +00:00]]
(0.1ms) rollback transaction
QuestionResponse Load (0.2ms) SELECT "question_responses".* FROM "question_responses" WHERE "question_responses"."quiz_result_id" IS NULL
Rendered quiz_results/_form.html.haml (5.0ms)
Rendered quiz_results/new.html.haml within layouts/application (6.8ms)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 113461968]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 113461968]]
Student Load (0.1ms) SELECT "students".* FROM "students" WHERE "students"."id" = 695735877 LIMIT 1
Completed 200 OK in 37ms (Views: 13.4ms | ActiveRecord: 2.4ms)
Use validates_associated: http://guides.rubyonrails.org/active_record_validations_callbacks.html#validates_associated

Resources