Rails HABTM has_many destroy error - ruby-on-rails

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

Has_many through relation "user_roles" does not exist

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

Dependent destroy to tables with more than one references

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

ActiveRecord::HasManyThroughOrderError: Cannot have a has_many :through association

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...

Ruby on Rails Nested Form

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.

RoR: Accessing :through attributes

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.

Resources