I have a has_and_belongs_to_many using has_many between users and workspaces.
user.rb
class User < ActiveRecord::Base
has_many :user_workspaces, dependent: :destroy
has_many :workspaces, through: :user_workspaces
before_destroy :delete_workspaces
def delete_workspaces
self.workspaces.each(&:destroy)
end
workspace.rb
class Workspace < ActiveRecord::Base
has_many :user_workspaces
has_many :users, through :user_workspaces
end
user_workspaces class and migration:
class UserWorkspace < ActiveRecord::Base
self.table_name = "user_workspaces"
belongs_to :user
belongs_to :workspace
end
class CreateUsersAndWorkspaces < ActiveRecord::Migration
def change
create_table :users_workspaces, id: false do |t|
t.belongs_to :user, index: true
t.belongs_to :workspace, index: true
t.boolean :admin, default: true
end
end
end
class RenameUsersWorkspaces < ActiveRecord::Migration
def change
rename_table('users_workspaces', 'user_workspaces')
end
end
I want this both test to pass:
should "destroy all associatios and objects" do
user = User.create!(attributes_for(:user))
w = user.workspaces.create!(attributes_for(:workspace))
user.destroy
assert UserWorkspace.all.empty? #asser there are no associations
end
which gives me the error
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column:
user_workspaces.: DELETE FROM "user_workspaces" WHERE
"user_workspaces"."" = ?
should "destroy association when destroying workspace" do
user = User.create!(attributes_for(:user))
w = user.workspaces.create!(attributes_for(:workspace))
w.destroy
assert UserWorkspace.all.empty?
end
How can I cover both scenarios? destroying an user destroys the association and the workspaces, destroying the workspace also destroys the association
According to http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html just need to ignore the join table.
Workspace.rb now has dependent destroy, to destroy only the join (intended behavior of destroy)
has_many :users, through: :user_workspaces,
inverse_of: :workspaces,
dependent: :destroy
then user.rb keeps the delete_workspaces functions, but adds dependent: :destroy, to also destroy the association.
user.rb
before_destroy: :delete_workspaces
has_many :workspaces, through: :user_workspaces,
dependent: :destroy
def delete_workspaces
self.workspaces.each(&:destroy)
end
now both test passes.
Related
Hello I try to do a has_many through relation but it's not working as I expect.
Here are my models
def User < ApplicationRecord
has_many :user_roles, class_name: "UserRole"
has_many :roles, through: :user_roles
end
def Role < ApplicationRecord
has_many :user_roles, class_name: "UserRole"
has_many :users, through: :user_roles
end
def UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
When I do this in rails console
User.roles
I've got the following error
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: relation "user_roles" does not exist)
I'm stuck with this error since 2 hours and no google result helped me ...
EDIT:
Here is my migration
class CreateUserRole < ActiveRecord::Migration
def change
create_table :user_role do |t|
t.belongs_to :user, type: :uuid
t.belongs_to :role, type: :uuid
end
end
end
I have a table Atribuition with 2 references from User table.
class Attribuition < ApplicationRecord
belongs_to :user, class_name: 'User', foreign_key: 'user_id'
belongs_to :not_rated, class_name: 'User', foreign_key: 'not_rated_id'
end
The User model:
class User < ApplicationRecord
has_many :attribuitions, dependent: :destroy
end
When i destroy an user marked in not_rated i want it to be destroyed, but it just happens when i destroied an user marked as user_id, then the attribute row is deleted. I wanna make dependent:: destroy to work for many references of same model. That is possible?
My migration is:
class CreateAttribuitions < ActiveRecord::Migration[5.2]
def change
create_table :attribuitions do |t|
t.references :user
t.references :not_rated, index: { unique: true }
t.timestamps
end
end
end
Edit:
First you do following change as rails use convention over configuration
class Attribuition < ApplicationRecord
- belongs_to :user, class_name: 'User', foreign_key: 'user_id'
+ belongs_to :user
end
Changes needed
When you mention has_many :attribuitions, dependent: :destroy by side of User model class_name will be Attribuition and foreign_key will be user_id stored in attributions table.
So if you need to destroy attribuitions related by foreign_key not_rated_id & user_id then you need following changes.
class User < ApplicationRecord
has_many :attribuitions, dependent: :destroy # default foreign_key is user_id
has_many :not_rated_attribuitions, foreign_key: 'not_rated_id', dependent: :destroy
end
In my rails app I'm trying to create a system that will reward users with badges for various achievements
created a table 'user_badges'
migration:
class CreateUserBadges < ActiveRecord::Migration[5.1]
def change
create_table :user_badges do |t|
t.references :user, foreign_key: true
t.references :badge, foreign_key: true
t.timestamps
end
end
end
model UserBadge:
class UserBadge < ApplicationRecord
belongs_to :user
belongs_to :badge
end
модель Badge:
class Badge < ApplicationRecord
has_many :users, through: :user_badges
has_many :user_badges
end
model User:
class User < ApplicationRecord
...
has_many :badges, through: :user_badges
has_many :user_badges
...
end
when I try to add a badge to the user:
b = Badge.create(title: 'first')
User.last.badges << b
I get this error:
ActiveRecord::HasManyThroughOrderError: Cannot have a has_many
:through association 'User#badges' which goes through
'User#user_badges' before the through association is defined.
also when I simply call:
User.last.badges
same error:
ActiveRecord::HasManyThroughOrderError: Cannot have a has_many
:through association 'User#badges' which goes through
'User#user_badges' before the through association is defined.
Define has_many association first then add through: association
class UserBadge < ApplicationRecord
belongs_to :user
belongs_to :badge
end
class Badge < ApplicationRecord
has_many :user_badges # has_many association comes first
has_many :users, through: :user_badges #through association comes after
end
class User < ApplicationRecord
...
has_many :user_badges
has_many :badges, through: :user_badges
...
end
Note, in case you mistakenly wrote first has_many 2 times, then it can reproduce this error too. E.g.
class User < ApplicationRecord
...
has_many :user_badges
has_many :badges, through: :user_badges
...
has_many :user_badges
end
# => Leads to the error of ActiveRecord::HasManyThroughOrderError: Cannot have a has_many :through association 'User#badges' which goes through 'User#user_badges' before the through association is defined.
Active Record should alert has_many being used two times IMHO...
I have three Models: Deal, Zipcode, DealIncludeZipcode.
Now, the association looks like below:-
Deal Model:
class Deal < ActiveRecord::Base
has_many :deal_include_zipcodes, dependent: :destroy
has_and_belongs_to_many :zipcodes, dependent: :destroy
accepts_nested_attributes_for :deal_include_zipcodes,:reject_if => :reject_include_zipcodes, allow_destroy: true
private
def reject_include_zipcodes(attributes)
if attributes[:deal_id].blank? || attributes[:zipcode_id].blank?
if attributes[:id].present?
attributes.merge!({:_destroy => 1}) && false
else
true
end
end
end
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
class DealIncludeZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
Now in view I have a checkbox,on unchecking it I can select multiple zipcode to select from DealIncludeZipcode.But when I save the data it is not saving.
I have used migration for joining Zipcode and Deal Model in which my exclude zipcode functionality is working correctly.
Please provide a soloution.I have tried various method but didn't got succeed.
The whole point of has_and_belongs_to_many is that you don't have a model which joins the two parts.
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
Would join through a "headless" table called deals_zipcodes. If you want to have a join model you need to use has_many :through instead.
class Deal < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :zipcodes, through: :deal_zipcodes
end
class DealZipcode < ActiveRecord::Base
belongs_to :deal
belongs_to :zipcode
end
class Zipcode < ActiveRecord::Base
has_many :deal_zipcodes, dependent: :destroy
has_many :deals, through: :deal_zipcodes
end
I think Max's right. So your migration should be
create_table :deals do |t|
t.string :name
...
end
create_table :zipcodes do |t|
t.string :zipcode
...
end
create_table :deals_zipcodes do |t|
t.belongs_to :deal, index: true
t.belongs_to :zipcode, index: true
end
And your models should be
class Deal < ActiveRecord::Base
has_and_belongs_to_many :zipcodes
end
class Zipcode < ActiveRecord::Base
has_and_belongs_to_many :deals
end
You should probably take a look at the ActiveRecord guide, where you'll find more explanation.
I'm new to rails and working in this app where an User can create many Events. Many Users can be invited to these Events, therefore I have the following Models:
User:
class User < ActiveRecord::Base
...
has_many :events, dependent: :destroy
end
Event:
class Event < ActiveRecord::Base
belongs_to :user
has_many :event_guest
has_many :guests, :through => :event_guest, :source => :user
end
Event_Guest:
class EventGuest < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
What I am looking for is being able to access (and add) the guest users to an event, for which I've tried all the variations I could thing of "Event.find(1).guests", only to get the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: event_guests.event_id: SELECT "users".* FROM "users" INNER JOIN "event_guests" ON "users"."id" = "event_guests"."user_id" WHERE "event_guests"."event_id" = ? AND "users"."event_id" = 1
My event_guest migration was the following:
create_table :event_guest do |t|
t.belongs_to :user, index: true
t.belongs_to :event, index: true
end
Like #Dharam mentioned, your naming convention is incorrect. You need to rename the event_guest table.
$ rails g migration rename_event_guest_table
And then the migration looks like this:
def change
rename_table :event_guest, :event_guests
end
You then need to update your event.rb model to be
class Event < ActiveRecord::Base
belongs_to :user
has_many :event_guests
has_many :guests, :through => :event_guests, class_name: 'User'
end
And your user.rb model:
class User < ActiveRecord::Base
...
has_many :event_guests, dependent: :destroy
has_many :events, through: :event_guests
end
Move the dependent destroy off the events association and to the event_guests relationship. You don't want to destroy the event just because one guest isn't going...
Try adding self.table_name = "event_guest" to EventGuest as that's what you have in migration which is contrary to rails expectation of "event_guests" from the model name.