I'm looking to set up a self-referencing has_and_belongs_to_many relationship using rails 4, with a postgres db.
Basically I've got a Single Inheritance Table set up called Resource, which holds People, Places, and Things in it. (This works beautifully.)
create_table :resources do |t|
t.string :name
t.string :type
t.text :description
end
I'm trying to create a 'has_and_belongs_to_many' relationship so that each Resource can have a series of 'owners', which will be an Relation of People. Each Person, in turn, will have a series of 'possessions'. Since it's a single table, I'll need to join the Resource table to itself.
My migration for the join table looks like this:
create_table :owners_possessions, id: false do |t|
t.integer :owner_id # the id of a person
t.integer :possession_id # the id of a place/thing owned by that person
end
Person.rb:
class Person < Resource
has_and_belongs_to_many :possessions, class_name: :resources,
join_table: :owners_possessions,
foreign_key: :owner_id,
association_foreign_key: :possession_id
end
Resource.rb:
class Resource < ActiveRecord::Base
has_and_belongs_to_many :owners, class_name: :people,
join_table: :owners_possessions,
association_foreign_key: :possession_id,
foreign_key: :owner_id
end
However, when running Resource.find(x).owners in the console, I get the following error message:
ActiveRecord::StatementInvalid: Could not find table 'resources_resources'
Which is perturbing because everything I've searched so far has pointed toward the join_table option as a way to get it looking at the right table.
Furthermore, running `Person.find(x).possessions' yields
NameError: uninitialized constant Person::Possession
What might I be missing here?
I can't reproduce the errors you posted, I suppose you altered your code somewhat.
Anyway, the class_name option in your associations should be the exact name of the other model. So 'Person' in singular form rather than :people:
class Person < Resource
has_and_belongs_to_many :possessions,
class_name: 'Resource',
join_table: :owners_possessions,
foreign_key: :possession_id,
association_foreign_key: :owner_id
end
class Resource < ActiveRecord::Base
has_and_belongs_to_many :owners,
class_name: 'Person',
join_table: :owners_possessions,
association_foreign_key: :possession_id,
foreign_key: :owner_id
end
Notice I also swapped the :foreign_key and :association_foreign_key values so they return the appropriate records.
Related
I've implemented a model with modified_by_id and modifies_id self-referencing attributes to persist "dirty" state of some models.
class CreateModels < ActiveRecord::Migration[5.0]
def change
create_table :models do |t|
t.string :name
t.modified_by_id :integer
t.modifies_id :integer
end
end
end
This double association has the intention of speeding (and simplifying) two queries
the latest version of things where(modified_by_id: nil)
the persisted version of things where(modifies_id: nil)
Everything works well when handling the ids manually, but I can't model it properly with relations.
has_one :modifies, class_name: 'Some::Model', foreign_key: 'modifies_id'
belongs_to :modified_by, class_name: 'Some::Model', foreign_key: 'modified_by_id'
Using it so populates only one of the ids when calling model.modifies = new_model
And this:
has_one :modifies, class_name: 'Some::Model', foreign_key: 'modifies_id'
belongs_to :modified_by, class_name: 'Some::Model', inverse_of: :modifies, foreign_key: 'modified_by_id'
pupulates only one model with the same id twice (modifes_id == modofies_by_id)
How should this be modeled?
Every post has a userview, and each userview has many users. I want one single many to many to have a simple .add() and .remove() function like django. How do I place the current_user into the many-to-many relationship of the views?
I found this:
#post.userview.users << current_user
But it brings up some SQL error. It's suggesting I add a post_id:
ERROR: column userviews.post_id does not exist
LINE 1: SELECT "userviews".* FROM "userviews" WHERE "userviews"."po...
^
HINT: Perhaps you meant to reference the column "userviews.posts_id".
After the answer, now the error is:
can't write unknown attribute `userview_id`
Because I changed the migrations around a little, userview has references to posts and users. Post has_one userview, has_many users through userview, userview has_many posts and has_many users.
create_table "posts", force: :cascade do |t|
t.integer "userview_id"
t.bigint "userviews_id"
t.index ["userviews_id"], name: "index_posts_on_userviews_id"
end
create_table "userviews", force: :cascade do |t|
t.bigint "users_id"
t.bigint "posts_id"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["posts_id"], name: "index_userviews_on_posts_id"
t.index ["users_id"], name: "index_userviews_on_users_id"
end
class User < ActiveRecord::Base
has_many :viewed_posts, through: :userview, class_name: 'Post'
...
class Userview < ApplicationRecord
belongs_to :post
belongs_to :user
end
class Post < ApplicationRecord
belongs_to :user
has_many :userviews
has_many :viewers, through: :userview, class_name: 'User'
...
Could not find the source association(s) "viewer" or :viewers in model Userview. Try 'has_many :viewers, :through => :userviews, :source => <name>'. Is it one of post or user?
Although, that's for unless #post.viewers.include?(current_user)
Not sure if I understood correctly what are you trying to achieve, but I'll try to fix your associations.
To associate 2 models you need to store foreign key in one of them. Model which stores the key belongs_to other model. Eg in posts table you have user_id column, it means Post belongs_to: user and User has_many :posts (or has_one :post if you need one-to-one association). For such association you can write:
user = User.first
post = Post.last
user.posts << post
If you want all this stuff to work automatically you should follow the convention about naming. Foreign keys should be in singular form, like user_id, not users_id
If you want many-to-many association you need to create an intermediate table, which stores both foreign keys. It can be done using simple direct has_and_belongs_to_many association or with more complex has_many through:
I suppose that in your case it should be:
class User < ActiveRecord::Base
has_many :userviews
# it is posts viewed by the user
# you need to specify class name because it differs from association name
has_many :viewed_posts, through: :userviews, class_name: 'Post'
# posts written by the user
has_many :posts
end
class Userview < ApplicationRecord
belongs_to :post
belongs_to :user
end
class Post < ApplicationRecord
belongs_to :user
has_many :userviews
has_many :viewers, through: :userviews, source: :user
end
It means that you need post_id and user_id columns in userviews table and user_id in posts table. Please, remove useless columns and add needed in migration. When you set it all correctly you'll be able to do
#post.viewers << current_user
to add current_user to viewers list. Corresponding userview instance is created automagically
Changed has_many :viewers, through: :userviews, class_name: 'User'
to has_many :viewers, through: :userviews, source: :user
(I'm translating the code as I write, so I apologize for any mistakes!)
I'm trying to implement a bidirectional self referential relation involving the Associates table and a join table Property. I tried following this post but something is still wrong.
The way it's supposed to work is, an associate can be the proprietor of zero or more associates, and consequently an associate may have zero or more proprietors (makes more sense in the context of the application).
So I created the Property model:
class CreateProperties < ActiveRecord::Migration
def change
create_table :properties do |t|
t.integer :proprietor_id
t.integer :property_id
t.timestamps null: false
end
end
end
So the table contains only the ids of one proprietor and one property, both associates, per entry.
Following the tutorial linked above, I came to this configuration:
Associate.rb:
...
has_many :properties
has_many :related_properties, :through => :properties
has_many :proprietors, :class_name => "Property", :foreign_key => "proprietor_id"
has_many :related_proprietors :through => :proprietors, :source => :associate
...
Property.rb:
belongs_to :associate
belongs_to :related_properties, :class_name => "Associate"
However when I try to use these relations (<% #associate.related_properties.each do |property| %>), I get this error:
PG::UndefinedColumn: ERROR: column properties.related_properties_id does not exist
LINE 1: ... INNER JOIN "propriedades" ON "associados"."id" = "proprieda...
^
: SELECT "associates".* FROM "associates" INNER JOIN "properties" ON "associates"."id" = "properties"."related_properties_id" WHERE "properties"."associate_id" = $1
Basically, the column names are wrong in the generated SQL: properties.related_properties_id should be properties.proprietor_id, and properties.associate_id should be properties.proprietor_id as well.
What have I done wrong, and how can I fix this code to get the correct relations?
You need to setup two seperate associations since the foreign key on Property depends on what the Associates role is.
class Associate
# defines relations where Associate is the "owning" party
has_many :properties_as_proprietor,
class_name: 'Property',
foreign_key: 'proprietor_id'
has_many :properties,
through: :properties_as_property,
source: :property # what to select on Property
# defines relations where Associate is the "owned" party
has_many :properties_as_property,
class_name: 'Property',
foreign_key: 'property_id'
has_many :proprietors,
through: :properties_as_proprietor,
source: :proprietor # what to select on Property
end
class Property
belongs_to :proprietor, class_name: 'Associate'
belongs_to :property, class_name: 'Associate'
end
I am having problems creating this association: Consider a model "Entry". I want entries to have many entries as parents and I want entries to have many entries as children. I want to realize this relation via a model I called "Association", so here is what I tried:
Migration:
class CreateAssociations < ActiveRecord::Migration[5.0]
def change
create_table :associations do |t|
t.integer :parent_id
t.integer :child_id
end
end
end
Association model:
class Association < ApplicationRecord
belongs_to :parent, class_name: 'Entry'
belongs_to :child, class_name: 'Entry'
end
so far it works. But now how do I use this to create two many-to-many relations on a model to itself?
class Entry < ApplicationRecord
# has many parent entries of type entry via table associations and child_id
# has many child entries of type entry via table associations and parent_id
end
This should work:
class Entry < ApplicationRecord
has_and_belongs_to_many :parents, class_name: 'Entry', join_table: :associations, foreign_key: :child_id, association_foreign_key: :parent_id
has_and_belongs_to_many :children, class_name: 'Entry', join_table: :associations, foreign_key: :parent_id, association_foreign_key: :child_id
end
I have those two Models, in a HABTM Relationship:
The Project is using Rails 4, so no attr_accessible tags
wine.rb
class Wine < ActiveRecord::Base
has_and_belongs_to_many :pairings, class_name: 'Food', join_table: 'foods_wines', association_foreign_key: 'food_id'
has_many :images, as: :attachable, class_name: 'Asset', dependent: :delete_all
end
food.rb
class Food < ActiveRecord::Base
has_and_belongs_to_many :wines, class_name: "Wine", join_table: "foods_wines", foreign_key: "food_id"
end
I created the Join Table with this migration:
create_table(:foods_wines, :id => false) do |t|
t.integer :food_id
t.integer :wine_id
end
add_index :foods_wines, [:food_id, :wine_id]
When I try to create the new Relation in the Rails Console, it does not seem to be saving the HABTM Relationship.
#wine.pairings.create(:name => "Seafood")
it does not seem to be saving the HABTM Relation -> When I restart the console, the relation is gone - I also checked inside the DB, where I get an empty table for the foods_wines table.
Am I missing something crucial here?
I think, you have to replace :
has_and_belongs_to_many :pairings, class_name: 'Food', join_table: 'foods_wines', association_foreign_key: 'food_id'
with :
has_and_belongs_to_many :pairings, class_name: 'Food', join_table: 'foods_wines', foreign_key: 'wine_id'
in wine.rb, because you have to specify the foreign key of this class (Wine).