Many to Many relationship :through giving 'Could not find the association' error - ruby

In my model an Item is created by a User and can be purchased by many Users, and a User can purchase many Items.
User, Item, and Purchase are defined, using AcvtiveRecord with superfluous details snipped for brevity as follows:
class User < ActiveRecord::Base
# various other fields
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
class Purchase < ActiveRecord::Base
belongs_to :item
belongs_to :user
# various other fields
end
and an rspec test also snipped as follows:
describe "user purchasing" do
it "should allow a user to purchase an item" do
a_purchase = Purchase.create!(:item => #item, # set up in `before :each`
:user => #user # set up in `before :each`
)
a_purchase.should_not eq(nil) # passes
#item.buyers.should include #user # fails
#user.purchased_items.should include #item # fails
end
end
This results in
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #item.buyers.should include #user
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model Item
Likewise if I swap around #file_item.buyers.should include #user and #user.purchased_items.should include #item I get the equivalent
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #user.purchased_items.should include #item
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model User
My migration looks like
create_table :users do |t|
# various fields
end
create_table :items do |t|
t.integer :creator_id # file belongs_to creator, user has_many items
# various fields
end
create_table :purchases do |t|
t.integer :user_id
t.integer :item_id
# various fields
end
What have I done wrong?

You have to specify the following.
class User < ActiveRecord::Base
has_many :purchases
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
has_many :purchases
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
Only when you specify
has_many :purchases
the model will be able to identify the association.

Related

Active Record has_many :messages not saving records properly

I'm building a sinatra app with Active record. The idea is to essentially have a custom email app. Here I have the models User and Message. A User has_many :messages and a Message belongs_to :user. This may be where I have the issue. I also have it set up for a Message belongs_to :user and has_many :users.
here are the models
Now when I create a message in the action controller I am attempting to use the shove methods to put the new message in a user's messages array. If I attempt to "share" this message with multiple users at once with all the user's id's in params( #user = User.find(id) and then user.messages << #new_message) the last user will have the message stored in it's .messages array. However only the last one to be iterated.
class Message < ActiveRecord::Base
belongs_to :user
has_many :users
end
class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: true
has_secure_password
has_many :messages
end
The idea is the writer "owns" the message but can share it with many users. Here are the tables
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :password_digest
end
end
end
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.string :message
t.string :author
t.integer :user_id
t.integer :user_ids
t.integer :share_id
t.string :title
t.timestamps
end
end
end
# action controller
new_params = {}
new_params[:message] = params["message"]
new_params[:title] = params["title"]
new_params[:author] = params["author"]
new_params[:user_id] = params["user_id"]
#message = Message.create(new_params)
# #share = Share.create
# #message.share_id = #share.id
response.map do |x|
x.messages << #message
x.save!
end
#all = User.all
#user = User.find_by(username: #message.author)
erb :"/user/sent"
I am fairly sure this is because my associations are not set up properly.
You need to have has-and-belongs-to-many relationship between users and messages to implement sharing multiple messages to multiple users. Create an additional record, e.g. MessageShare and do has many to it from both sides:
class MessageShare < ActiveRecord::Base
belongs_to :user
belongs_to :message
end
class Message < ActiveRecord::Base
has_many :message_shares
end
class User < ActiveRecord::Base
has_many :message_shares
end
message_shares table should have user_id and message_id integer columns.

How is the following Rails model should look like in Sequel?

I've the following two models:
class Dispute < ApplicationRecord
belongs_to :accuser, class_name: 'User', optional: true
belongs_to :defendant, class_name: 'User', optional: true
end
class User < ApplicationRecord
end
Here's the migration for Dispute:
class CreateDisputes < ActiveRecord::Migration[5.0]
def change
create_table :disputes do |t|
t.references :accuser
t.references :defendant
end
end
end
This is how they behave in Rails:
Dispute.first.accuser
# => <# User>
Dispute.first.defendant
# => <# User>
In Sequel, I'm supposed to use many_to_one, but does that mean that Sequel User model should have a corresponding one_to_many? Can't seem to get it to work.
This should work:
Sequel.migration do
change do
create_table(:disputes) do
primary_key :id
foreign_key :accuser_id, :users
foreign_key :defendant_id, :users
end
end
end
class Dispute < Sequel::Model
many_to_one :accuser, :class=>:User
many_to_one :defendant, :class=>:User
end

Trouble with cascading in Active Record models

I have these active record models in my application
class Artist < ActiveRecord::Base
has_many :albums, :dependent => :delete_all, autosave: true
end
class Album < ActiveRecord::Base
attr_accessor :album_artist
has_many :tracks, :dependent => :delete_all, autosave: true
has_many :covers, :dependent => :delete_all, autosave: true
belongs_to :artist
end
class Track < ActiveRecord::Base
belongs_to :album
end
class Cover < ActiveRecord::Base
belongs_to :album
end
I'm trying, in my application, when i delete an Artist, his albums and, in consequence, the tracks and covers of his albums, get all deleted, in a cascade reaction.
The way it's implemented today, when i delete the Artist, only the Album is deleted too, leaving orphan records in my database.
Am i doin' anything wrong?
Instead of :dependent => :delete_all you need to configure:
:dependent => :destroy_all
Because delete will just delete all its associated objects directly from the database without calling their destroy method (what break cascading).
You might want to read 4.1.2.4 in http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference

ActiveRecord::HasManyThroughAssociationPolymorphicSourceError

I need a player to have many structures and the structure to belong to the player. Structure is a polymorphic relationship.
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structures, :through => player_structures
end
class PlayerStructures < ActiveRecord::Base
belongs_to :structure, polymorphic: true
belongs_to :player
end
class StructureA < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
class StructureB < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
But if I pull out Player.first and ask for its structures, it gives:
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'Player#structures' on the polymorphic object 'Structure#structure'.
But it should be able to generate a SQL query where it finds all player_structures with its id, then fetches the structure based on the structure_id and structure_type. Why does this fail and how can I validly construct a polymorphic join table?
UPDATE
If I do what I want it to do manually, it works:
player_structures.collect(&:structure)
Rails, y u no do that?
I think you need to be more specific in defining your relationships in your Player model. For example:
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structureas, :through => player_structures, :source => :structure, :source_type => 'StructureA'
has_many :structurebs, :through => player_structures, :source => :structure, :source_type => 'StructureB'
end
Then you can make a method that'll return all the structures defined in the relationships instead of having to access each one individually.

Polymorphic has_many self-referential

I have A number of models (Article, Video, Photo)
Now I am trying to create a related_to association, such that
An article can have many other articles, videos and photos related to it. As can videos and photos.
Heres what I have tried:
module ActsAsRelatable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_relatable
has_many :related_items, :as => :related
has_many :source_items, :as => :source, :class_name => 'RelatedItem'
end
end
end
class RelatedItem < ActiveRecord::Base
belongs_to :source, :polymorphic => true
belongs_to :related, :polymorphic => true
end
Then I have added acts_as_relatable to my three models (Article, Video, Photo) and included the module in ActiveRecord::Base
When trying in ./script/console I get it to add the related items and the ids work correctly however the source_type and related_type are always the same (the object that related_items was called from) I want the related_item to be the other model name.
Any ideas anyone?
I would use the has many polymorphs plugin since it supports double sided polymorphism you can do something like this:
class Relating < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
belongs_to :relative, :polymorphic => true
acts_as_double_polymorphic_join(
:owners => [:articles, :videos, :photos],
:relatives => [:articles, :videos, :photos]
)
end
and don't forget the db migration:
class CreateRelatings < ActiveRecord::Migration
def self.up
create_table :relating do |t|
t.references :owner, :polymorphic => true
t.references :relative, :polymorphic => true
end
end
def self.down
drop_table :relatings
end
end
I don't know if "Relating" is a good name, but you get the idea. Now an article, video and photo can be related to another article, video or photo.

Resources