I've a simply rails application. It contains two models. Employee and EmployeeManager. I Basically want an Employee to have one or more Managers (other Employees) and I wish to be able to query these as you would expect.
Here's my Employee:
class Employee < ActiveRecord::Base
attr_accessible :name
has_many :employee_managers
has_many :managers, through: :employee_managers
has_many :employees, through: :employee_managers
end
And the manager model:
class EmployeeManager < ActiveRecord::Base
attr_accessible :employee_id, :manager_id
belongs_to :employee, foreign_key: "employee_id"
belongs_to :manager, class_name: "Employee", foreign_key: "manager_id"
end
This looks OK to me, however when I go to query employees and managers off an Employee Rails is using the same column (employee_id=X):
irb(main):008:0> Employee.first
Employee Load (0.3ms) SELECT "employees".* FROM "employees" LIMIT 1
=> #
irb(main):009:0> Employee.first.managers
Employee Load (0.1ms) SELECT "employees".* FROM "employees" LIMIT 1 Employee Load (0.1ms) SELECT "employees".* FROM "employees" INNER JOIN "employee_managers" ON "employees"."id" = "employee_managers"."manager_id" WHERE "employee_managers"."employee_id" = 1
=> []
irb(main):010:0> Employee.first.employees
Employee Load (0.4ms) SELECT "employees".* FROM "employees" LIMIT 1 Employee Load (0.3ms) SELECT "employees".* FROM "employees" INNER JOIN "employee_managers" ON "employees"."id" = "employee_managers"."employee_id" WHERE "employee_managers"."employee_id" = 1
=> []
How do I fix this last query? It should be using manager_id not employee_id!
Thanks in advance :)
Set the foreign_key in the Employee model, and add and inverse relation (its the same Model)
class Employee < ActiveRecord::Base
has_many :employee_managers
has_many :managers, through: :employee_managers
has_many :manager_employees, :class_name => "EmployeeManager", :foreign_key => "manager_id"
has_many :employees, through: :manager_employees, :source => "Employee"
end
Other changes:
You don't need attr_accessible :name or attr_accessible :employee_id, :manager_id. Just add attr_accessible for fields that are not in the database.
Related
I’m using Rails 4.2. I have the following user model with a couple of has_many associations
class User < ActiveRecord::Base
…
has_many :roles, through: :roles_users
has_many :addresses, dependent: :destroy, as: :addressable, inverse_of: :addressable
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :roles_users
class RolesUser < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
alias :user :addressable
I would like to find all users of a specific role without any addresses. I thought the below would do it
> users = User.includes(:roles, :addresses).where(:roles => {:name => 'User'}, :addresses => {:user_id => nil})
But when I check the results, I’m still getting results that have addresses …
2.7.1 :012 > users.last.addresses.count
…
=> 2
What’s the proper way to write a finder that queries these two has_many associations?
Checking for children records with a nil parent id is like the way of doing this in Rails 4. But if that doesn't work, you could use the NOT IN clause combination:
User
.where
.not(
id: User
.joins(:addresses, :roles)
.where(roles: { name: 'User' })
.select(:id)
)
It's basically filtering out by the user id all those user rows that have an address associated, have a role, and the role name is exactly "User". You end up with a SQL query like this:
SELECT "users".*
FROM "users"
WHERE "users"."id" NOT IN (
SELECT "users"."id"
FROM "users"
INNER JOIN "roles_users" ON "roles_users"."user_id" = "users"."id"
INNER JOIN "roles" ON "roles"."id" = "roles_users"."role_id"
WHERE "roles"."name" = 'User'
)
I have the models Province and User and I need to stop a Province from being deleted if a User has that Province in it's address.
I can do User.province thanks to the following concern included in the model User:
module MyAddressable
extend ActiveSupport::Concern
included do
has_one :address, as: :addressable, dependent: :destroy
has_one :city, through: :address
has_one :province, through: :city
has_one :zone, through: :city
accepts_nested_attributes_for :address, reject_if: :reject_address, allow_destroy: true
end
end
I'm trying to establish the relationship between Province and User to be able to do something like Province.users, in the following way:
has_many :users, through: :myaddresable
With the following result:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :myaddresable in model Province
Same thing if I try to define the relationship as
has_many :users, through: :addressable
Can this be done? If so, what would be the right way to do it?
has_many :users, through: :addressable doesn't work because Province model has no knowledge about the Address model.
We can build the relationship between the Province model and User model through Address model.
The following setup works in rails 6
User Model
class User < ApplicationRecord
has_one :address, as: :addressable, dependent: :destroy
has_one :city, through: :address
has_one :province, through: :city
end
Province Model
class Province < ApplicationRecord
has_many :cities
has_many :users, through: :cities
end
City Model
class City < ApplicationRecord
has_many :addresses
has_many :users,
through: :addresses,
source: :addressable,
source_type: 'User'
belongs_to :province
end
Address Model
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true
belongs_to :city
end
Let's assume the migrations are defined correctly as per the model associations. Now the following queries work...
irb(main): > User.first.province
DEBUG -- : User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
DEBUG -- : Province Load (0.3ms) SELECT "provinces".* FROM "provinces" INNER JOIN "cities" ON "provinces"."id" = "cities"."province_id" INNER JOIN "addresses" ON "cities"."id" = "addresses"."city_id" WHERE "addresses"."addressable_id" = $1 AND "addresses"."addressable_type" = $2 LIMIT $3 [["addressable_id", 1], ["addressable_type", "User"], ["LIMIT", 1]]
irb(main): > Province.first.users
DEBUG -- : Province Load (0.5ms) SELECT "provinces".* FROM "provinces" ORDER BY "provinces"."id" ASC LIMIT $1 [["LIMIT", 1]]
DEBUG -- : User Load (0.5ms) SELECT "users".* FROM "users" INNER JOIN "addresses" ON "users"."id" = "addresses"."addressable_id" INNER JOIN "cities" ON "addresses"."city_id" = "cities"."id" WHERE "cities"."province_id" = $1 AND "addresses"."addressable_type" = $2 [["province_id", 1], ["addressable_type", "User"]]
In your case, as the MyAddressable concern is already included in the User model, only the other associations and migrations need to be defined.
Hope this helps. Thank you.
I've got model user and role
class User < ApplicationRecord
rolify strict: true
has_many :roles, through: :users_roles
has_associated_audits
class Role < ApplicationRecord
has_and_belongs_to_many :users, join_table: :users_roles
audited associated_with: :users, join_table: :users_roles
When I create a new role, I've got the error:
2.4.4 :373 > User.first.add_role Role.pi, ProjectRequest.find(319)
User Load (0.7ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
ProjectRequest Load (0.6ms) SELECT `project_requests`.* FROM `project_requests` WHERE `project_requests`.`id` = 319 LIMIT 1
Role Load (0.6ms) SELECT `roles`.* FROM `roles` WHERE `roles`.`name` = 'pi' AND `roles`.`resource_type` = 'ProjectRequest' AND `roles`.`resource_id` = 319 ORDER BY `roles`.`id` ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO `roles` (`name`, `resource_type`, `resource_id`, `created_at`, `updated_at`) VALUES ('pi', 'ProjectRequest', 319, '2018-06-19 11:40:13', '2018-06-19 11:40:13')
(54.3ms) ROLLBACK
NoMethodError: undefined method `primary_key' for User::ActiveRecord_Associations_CollectknowProxy:Class
I don't really now whats the problem, did I wrongly specified something?
Look like there is issue with association if it is has_and_belongs_to_many :users and join_table is users_roles on that case in roles table also it should be has_and_belongs_to_many :roles, join_table: :users_roles, and this will make HABM relationship.
I've resolved the problem by this tutorial:
http://blog.flatironschool.com/why-you-dont-need-has-and-belongs-to-many/
What I did was that I removed the HABTM relation and created model for the joining table.
class User < ApplicationRecord
rolify strict: true
has_many :users_roles
has_many :roles, through: :users_roles, dependent: :destroy
has_associated_audits
class Role < ApplicationRecord
has_many :users_roles
has_many :users, through: :users_roles, dependent: :destroy
audited associated_with: :users
class UsersRole < ApplicationRecord
# audited associated_with: :role
audited associated_with: :user
belongs_to :user
belongs_to :role
end
Now when the audits are created with change at the UsersRole instance.
The problem is that, the change's contains only the ids of destroyed columns, so you cannot figure, what it was.
This problem is partially solved there: https://github.com/collectiveidea/audited/issues/72#issuecomment-398756380
Ok, check this weirdness out. I have two types of users, leaders and followers (who each have their own subclasses for reasons that please for the love of god I don't want to go into and moreover are not germaine to this discussion).
class Admin < Account
has_many :leader_follower_relationships, foreign_key: :leader_id, dependent: :destroy
has_many :followers, through: :leader_follower_relationships
end
class Follower < Account
has_one :follower_leader_relationship, class_name: "LeaderfollwerRelationship",
dependent: :destroy
has_one :leader, through: :follower_leader_relationship
end
class LeaderFollowerRelationship < ActiveRecord::Base
belongs_to :follower, class_name: "Follower", foreign_key: :artist_id
belongs_to :leader, class_name: "Admin", foreign_key: :leader_id
end
Anyway, I can establish the relationship using a has_one, but I can't update it:
follower1.leader = leader1
(0.3ms) BEGIN
Account Exists (1.1ms) SELECT 1 AS one FROM "accounts" WHERE "accounts"."auth_token" IS NULL LIMIT 1
Account Exists (0.4ms) SELECT 1 AS one FROM "accounts" WHERE "accounts"."email_address" = 'leader1#example.com' LIMIT 1
SQL (0.6ms) ...
(0.5ms) COMMIT
follower1.leader = leader2
(0.3ms) BEGIN
(1.8ms) UPDATE "leader_follower_relationships" SET "leader_id" = 3 WHERE "leader_follower_relationships"."" IS NULL
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PGError: ERROR: zero-length delimited identifier at or near """"
LINE 1: ...ader_id" = 3 WHERE "leader_follower_relationships"."" IS NULL
If my follower can have many leaders (using has_many), I can both create and update:
class Follower < Account
has_many :follower_leader_relationships, class_name: "LeaderfollwerRelationship",
dependent: :destroy
has_many :leaders, through: :follower_leader_relationships
end
These commands work in succession:
follower1.leaders = [leader1]
follower1.leaders = [leader2]
The ActiveRecord error makes me think that the table for your LeaderFollowerRelationship model does not have an id column. If that's not it, can you post the related bits of schema.rb?
I have an app in which projects belong to one owner (a user) and have and belong to many members (users).
I have the following code set up in the models to handle these associations. Note, project has owner_id in its database but not member_id
project model code
attr_accessible :description, :name, :owner_id, :avatar_url
belongs_to :owner, foreign_key: :owner_id, class_name: "User"
has_and_belongs_to_many :members, class_name: "User"
user model code
has_and_belongs_to_many :projects
has_many :owned_projects, class_name: "Project", foreign_key: "owner_id"
has_many :associated_projects, class_name: "Project", foreign_key: "member_id"
On a given project, I can call p.owner but not p.members. And I for a user I can call u.owned_projects or u.projects but not u.associated_projects.
When I try those commands, I get the following errors
1.9.3p194 :003 > p.members
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "projects_users" ON "users"."id" = "projects_users"."user_id" WHERE "projects_users"."project_id" = 3
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: projects_users: SELECT "users".* FROM "users" INNER JOIN "projects_users" ON "users"."id" = "projects_users"."user_id" WHERE "projects_users"."project_id" = 3
and
1.9.3p194 :007 > ryan.associated_projects
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."member_id" = 14593
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: projects.member_id: SELECT "projects".* FROM "projects" WHERE "projects"."member_id" = 14593
How can I set up these models so they'll work as I intend?
Note: I modeled my code after - Two has_many links between the same models but it hasn't helped.
You have to have a has_many_and_belongs_to relationship in your User and Project models. Also you need a intermediate table inbetween. http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
Class Project < ActiveRecord::Base
attr_accessible :description, :name, :owner_id, :avatar_url
belongs_to :owner, foreign_key: :owner_id, class_name: "User"
has_and_belongs_to_many :members, class_name: "User"
end
Class User < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :owned_projects, class_name: "Project", foreign_key: "owner_id"
end
As said, you will have to have a middle table between member Project and User(member).
You had that error when you tried to see project members: no such table: projects_users
Projects_Users Table
user_id | project_id