I have 2 models with has_one and has_many associations.
realm.rb
class Realm < ActiveRecord::Base
has_one :realm_type, foreign_key: "id"
end
realm_type.rb
class RealmType < ActiveRecord::Base
has_many :realms, foreign_key: "realm_type_id"
end
But when i preforming sql request Realm.find(1).realm_type in rails console, i get
Realm Load (0.3ms) SELECT "realms".* FROM "realms" WHERE "realms"."id" = $1 [["id", 1]]
RealmType Load (0.3ms) SELECT "realm_types".* FROM "realm_types" WHERE "realm_types"."id" = $1 LIMIT 1 [["id", 1]]
As you see, it ignores foreign_key: "realm_type_id" for has_many association in realm_type.rb
UPD 1:
Replaced has_many with belongs_to, still get the same result
Shouldn't you be using belongs_to?
class RealmType < ActiveRecord::Base
has_many :realms, foreign_key: "realm_type_id"
end
class Realm < ActiveRecord::Base
belongs_to :realm_type
end
Related
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 have m:n association with counter cache. I want delete user and remove appropriate records from the users_items association. The sql query which should remove records from users_items association is wrong.
class User < ApplicationRecord
has_many :users_items, class_name: 'UsersItem', dependent: :destroy
has_many :items, through: :users_items
end
class Item < ApplicationRecord
has_many :users_items, class_name: 'UsersItem', dependent: :destroy
has_many :users, through: :users_items
end
class UsersItem < ApplicationRecord
belongs_to :user, counter_cache: true
belongs_to :item, counter_cache: true
end
u = User.last
u.destroy
(0.3ms) BEGIN
UsersItem Load (0.4ms) SELECT "users_items".* FROM "users_items" WHERE "users_items"."user_id" = $1 [["user_id", 41]]
UsersItem Destroy (1.9ms) DELETE FROM "users_items" WHERE "users_items"."" IS NULL
(0.3ms) ROLLBACK
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: zero-length delimited identifier at or near """")
LINE 1: DELETE FROM "users_items" WHERE "users_items"."" IS NULL
The problem was with the users_items table. The table was created without primary key. It started working when I added the primary id.
Myth
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
I just have found a strange behavior of Active Record, where I am not sure if I am the problem or Active Record ;-).
I am using: Rails 4, Ruby 2.0
Here the models:
class User < ActiveRecord::Base
has_many :conversation_participants, dependent: :destroy
has_many :conversations, through: :conversation_participants
end
class ConversationParticipant < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Conversation < ActiveRecord::Base
has_many :conversation_participants, dependent: :destroy
has_many :participants, through: :conversation_participants, source: :user
end
Pretty straight forward besides the has_many :participants, which refers to user over source:.
Normally, it is possible to call collection_singular_ids on an has_many associated object. Like in this case:
User.find(1).conversation_ids
This works perfectly, I get back an array of conversations ids. But from the other side:
Conversation.find(1).participant_ids
I alway getting back nil. When I go back to the standard for the has_many:
class Conversation < ActiveRecord::Base
has_many :conversation_participants, dependent: :destroy
has_many :users, through: :conversation_participants
end
Conversation.find(1).user_ids
will work fine.
When I look into the resulting queries, I see what the reason could be:
has_many :users creates:
SELECT "users".id FROM "users" INNER JOIN "conversation_participants" ON "users"."id" = "conversation_participants"."user_id" WHERE "conversation_participants"."conversation_id" = $1 [["conversation_id", 29]]
has_many :participants creates:
SELECT "users".* FROM "users" INNER JOIN "conversation_participants" ON "users"."id" = "conversation_participants"."user_id" WHERE "conversation_participants"."conversation_id" = $1 [["conversation_id", 29]]
in the participants case I get a SELECT "users".* instead SELECT "users".id
(using class_name: "User" has also not helped)
Bug or human error?
I have the following classes
class State < ActiveRecord::Base
has_many :cities
has_many :products, as: :geography
end
class City < ActiveRecord::Base
has_many :neighborhoods
has_many :products, as: :geography
end
class Neighborhood < ActiveRecord::Base
has_many :products, as: :geography
end
class Product < ActiveRecord::Base
belongs_to :geography, polymorphic: true
end
class User < ActiveRecord::Base
belongs_to :state
end
And obviously I have another class User. The user just has permissions for see the products in his/her state (except if the user is admin, in which case he/she can see all the products) How I can get all the products of a User?
I tried to add some has_many, :through to State as follows
has_many :cities_activities, through: :cities, source: :products
has_many :neighborhoods_activities, through: :neighborhoods, source: :products
def owned_activities
self.products + self.cities_activities + self.neighborhoods_activities
end
but owned_activities returns an Array not a ActiveRecord::Relation (I need some way of return a ActiveRecord::Relation, so I can apply on it chained scopes).
I am patching the code with if-blocks and the code is getting messing and ugly, How can I do this in a clean, rails way?
#JTG suggested using merge, but apparently it don't work in a nested way (or for more of three tables/models):
Started GET "/api/products.json?order=total_cost&page=1&per_page=18" for 127.0.0.1 at 2014-07-20 09:27:29 -0500
Processing by ProductsController#index as JSON
Parameters: {"order"=>"total_cost", "page"=>"1", "per_page"=>"18"}
Geokit is using the domain: localhost
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Role Load (0.3ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 ORDER BY "roles"."id" ASC LIMIT 1 [["id", 5]]
State Load (0.2ms) SELECT "states".* FROM "states" WHERE "states"."id" = $1 ORDER BY "states"."id" ASC LIMIT 1 [["id", 17]]
(9.2ms) SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "polygons" ON "products"."geography_id" = "neighborhoods"."id
" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1 [["geography_id", 17], ["geography_type", "State"], ["states_id", 17]]
PG::DuplicateAlias: ERROR: table name "cities" specified more than once
: SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "neighborhoods" ON "products"."geography_id" = "neighborhoods"."id" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1
ERROR: table name "cities" specified more than once
-- Clase:
You can't use merge will combine the queries with AND and what you need is a SQL OR query. That is unfortunately not supported by any ActiveRecord method. You can use arel to create this query, but as it will be very complex and contain a lot of subqueries do I find it easier to just find the id's you need with normal queries and then use those.
To do that can you implement something like this in State.
class State < ActiveRecord::Base
has_many :cities
has_many :neighborhoods, through: :cities
has_many :products, as: :geography
has_many :city_products, through: :cities, source: :products
has_many :neighborhood_products, through: :neighborhoods, source: :products
def all_products
state_product_ids = product_ids
city_product_ids = city_products.pluck(:id)
neighborhood_product_ids = neighborhood_products.pluck(:id)
all_product_ids = [state_product_ids, city_product_ids, neighborhood_product_ids].flatten.uniq
Product.where(id: all_product_ids)
end
end
You can now query a user for all products like this user.state.all_products.
A polymorphic association is not suited optimally for these relationships...
You can use a nested has_many.
class State < ActiveRecord::Base
has_many :cities
has_many :products, through: :cities
end
class City < ActiveRecord::Base
has_many :neighborhoods
has_many :products, through: :neighborhoods
end
class Neighborhood < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :neighborhood
end
class User < ActiveRecord::Base
belongs_to :state
end
Then you should be able to run user.state.products
Even better would be to add has_many :products, through: :state to user.rb so you could run user.products