In ActiveAdmin, when I want to delete a user I have the following error :
ActiveRecord::InvalidForeignKey in Admin::UsersController#destroy
PG::ForeignKeyViolation: ERROR: update or delete on table "users" violates foreign key constraint "fk_rails_b080fb4855" on table "notifications" DETAIL: Key (id)=(15) is still referenced from table "notifications". : DELETE FROM "users" WHERE "users"."id" = $1
I already have has_many :notifications, dependent: :destroy in my User model so I don't understand the error.
Notification model:
class Notification < ActiveRecord::Base
before_validation :check_modeles
validates :message, presence: true
validates :modele, presence: true
validates :user_id, presence: true
validates :contact_id, presence: true
validates_associated :user
belongs_to :user
belongs_to :contact, :class_name => "User", :foreign_key => "contact_id"
User model :
class User < ActiveRecord::Base
before_validation :check_genres
before_validation :set_image
before_validation :init_attrs
before_create :set_name
before_create :create_mangopay
after_create :set_centres_interets
after_create :set_preferences_musicales
after_create :check_on_create
after_update :check_on_update
before_update :create_mangopay_bank_account
before_destroy :remove_contact_notifications
acts_as_mappable :default_units => :kms,
:lat_column_name => :last_lat,
:lng_column_name => :last_lng
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
has_one :fil, dependent: :destroy
has_one :preferences_voyage, dependent: :destroy
has_one :verification, dependent: :destroy
has_many :badges, dependent: :destroy
has_many :favoris , dependent: :destroy
has_many :centres_interets, dependent: :destroy
has_many :preferences_musicales, dependent: :destroy
has_many :recommandations, dependent: :destroy
has_many :reputations, dependent: :destroy
has_many :reservations, dependent: :destroy
has_many :routes, dependent: :destroy
has_many :trajets_reguliers, dependent: :destroy
has_many :vehicules, dependent: :destroy
has_many :contact_notifications, foreign_key: 'contact_id', class_name: 'Notification', dependent: :destroy
has_many :notifications, dependent: :destroy
has_many :recherches, dependent: :destroy
has_many :blocages, dependent: :destroy
has_many :messageries, dependent: :destroy
has_many :messages, dependent: :destroy
validates :date_naissance, presence: true
validates :first_name, presence: true
validates :last_name, presence: true
Maybe the has_many :notifications, dependent: :destroy only affect the user and not the contact? If so, how do i fix it?
Ok, maybe your best bet is to just remove the foreign key constraints.
Create a migration with the following lines
remove_foreign_key :notifications, :users
remove_foreign_key :notifications, column: :contact_id
Rails doesn't require the foreign key constraints, and they're clearly not helping here.
You can also remove it by name...
remove_foreign_key :notifications, name: :fk_rails_b080fb4855
In your PostgreSQL database table notifications you have two different foreign keys that relate to users the user_id and the contact_id and the dependent: destroy only relates to the user_id.
Add another has_many to your User model to cover the second association
has_many :contact_notifications, foreign_key: 'contact_id', class_name: 'Notification', dependent: :destroy
Also, ensure that you're using the destroy method when you're deleting a user
#user.delete # will NOT run callbacks
#user.destroy # will run callbacks including destroying dependent records
To be sure that the issue is related to contact_id you can try removing those records with a manual process
class User
before_destroy :remove_contact_notifications
def remove_contact_notifications
Notification.where(contact_id: id).destroy_all
Notification.where(user_id: id).destroy_all
end
end
Related
I recently updated my app from ruby 2.5.1 to 2.6.3 and from rails 5.0.7.2 to 5.2.3
I'm experiencing an issue with the update method of my user controller.
I got undefined method 'delete_if' for #<String:0x00007fac73d38c78> Did you mean? delete delete!
# PATCH/PUT /users/:id
def update
if #user.update(user_params)
redirect_to company_users_path(#user.company), notice: 'User was successfully updated.'
else
render :edit
end
end
EDIT this is my user model
class User < ApplicationRecord
paginates_per 10
extend Enumerize
has_many :user_challenges, dependent: :destroy
has_many :challenges, through: :user_challenges
has_many :user_events, dependent: :destroy
has_many :events, through: :user_events
has_and_belongs_to_many :objectives
has_one :binome_as_invited, class_name: 'Binome', foreign_key: 'invited_user_id', dependent: :destroy
has_one :binome_as_inviting, class_name: 'Binome', foreign_key: 'inviting_user_id', dependent: :destroy
belongs_to :company, optional: true
has_many :actions, dependent: :destroy
has_many :hello_messages, foreign_key: 'target_id'
has_many :custom_challenges, foreign_key: 'target_id'
has_many :sent_challenges, class_name: 'CustomChallenge', foreign_key: 'author_id'
has_many :received_challenges, class_name: "CustomChallenge", foreign_key: "target_id"
has_many :sent_challenges, class_name: "CustomChallenge", foreign_key: "author_id"
has_many :binome_invitations, foreign_key: "invited_user_id"
has_many :device_tokens
delegate :challenges, to: :company, prefix: true
acts_as_paranoid
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
include DeviseTokenAuth::Concerns::User
attr_accessor :secret_code
validate :company_code, on: :create
validates :first_name, :last_name, :phone, :age, :ki_count, presence: true
ROLES = ['admin', 'company_admin', 'user', 'inactive']
enumerize :role, in: ROLES
scope :with_avatar, -> { where.not(avatar: 0) }
could it be acts_as_paranoid gem? Or maybe a gem I need to update? It's definitely since I update rails or ruby or maybe a single gem.
EDIT2 the last item of the stacktrace seems to be linked to the gem devise_token_auth
devise_token_auth (1.1.0) app/models/devise_token_auth/concerns/user.rb:218:in `destroy_expired_tokens'
I am working on a Rails app with Sqlite and have a users table associated with several other tables. When trying to rename a column in Users, I'm getting the subject error when running rails db:migrate.
I see a lot of posts here with similar issues, but none has worked. Specifically, the common remedy seems to be to use "dependent: :destroy" on all has_many and has_one associations. I am doing this but am still getting the error.
What am I doing wrong?
Below is my code:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile, dependent: :destroy
has_many :bikes, dependent: :destroy
has_many :bookings, dependent: :destroy
has_many :rented_bikes, through: :bookings, source: :bike
has_many :conversations, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :liked_bikes, through: :likes, :source => :bike
has_many :viewed_bikes, through: :views, :source => :bike
has_many :views, dependent: :destroy
has_many :reviews, dependent: :destroy
end
class Profile < ApplicationRecord
belongs_to :user
end
class Bike < ApplicationRecord
belongs_to :user
has_many :images, dependent: :destroy
has_many :bookings, dependent: :destroy
has_many :booked_users, through: :bookings, source: :user
has_many :conversations, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :liking_users, :through => :likes, :source => :user
has_one :amenity, dependent: :destroy
has_many :places, dependent: :destroy
has_many :views, dependent: :destroy
end
class Booking < ApplicationRecord
belongs_to :bike
belongs_to :user
has_one :review, dependent: :destroy
validates :date_start, presence: true
validates :date_end, presence: true
validates :user_id, presence: true
end
class Conversation < ApplicationRecord
belongs_to :user
belongs_to :bike
has_many :messages, dependent: :destroy
end
class Like < ApplicationRecord
belongs_to :user
belongs_to :flat
end
class View < ApplicationRecord
belongs_to :user
belongs_to :flat
end
class Review < ApplicationRecord
belongs_to :user
belongs_to :booking
end
Migration:
class ChangeCustomerIdToUserId < ActiveRecord::Migration[5.1]
def change
rename_column :users, :customer_id, :client_id
end
end
You have a couple problems happening at once:
SQLite doesn't support renaming columns so the ActiveRecord driver implements column renaming the hard way: create a new table with the new column names, copy all the data, drop the original table, rename the new one. Note that this has recently changed so the latest SQLite does support renaming columns in-place.
You have foreign keys in other tables that reference your users table.
(2) is what is triggering your error during your migration: you can't drop a table (see (1)) when there are foreign keys referencing it since dropping the table would violate those foreign keys.
The solution is to drop all the offending FKs in your migration, then do the rename_column, and then add all the FKs back again. Another option would be to try to turn off FKs and turn them back on in your migration, something like:
connection.execute("PRAGMA defer_foreign_keys = ON")
connection.execute("PRAGMA foreign_keys = OFF")
rename_column :users, :customer_id, :client_id
connection.execute("PRAGMA foreign_keys = ON")
connection.execute("PRAGMA defer_foreign_keys = OFF")
might work.
There was a commit made to Rails three months ago that should fix this problem but I don't think it has made it into any release version yet.
I have an issue with my rails project. There is a user that is not being returned when trying to fetch the record using find(:id) while the record is being fetched when using User.find_by(:email)
Here is an example:
User.find(333)
ActiveRecord::RecordNotFound: Couldn't find User with 'id'= 333
```User.find_by(email: "example#me.com")
=> #<User id: 333, email: "example#me.com", created_at: "2017-10-06 01:30:13", updated_at: "2018-01-03 22:54:31, ..."
```
BTW, I am using postgresql, and the project is hosted on aptible.
Thanks in advance.
Update
It is really strange. I was able to update another user's id to 333 without getting a conflict error. However I am not able to update any field in the original user with id = 333
Replies to comments:
User.find(333)
D, [2018-01-09T01:14:22.070276 #127] DEBUG -- : User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 333]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=333```
User.find_by(email: "example#me.com")
D, [2018-01-09T01:38:32.350705 #127] DEBUG -- : User Load (1.5ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'example#me.com' LIMIT 1
=> #<User id: 333, email: "example#me.com", created_at: "2017-10-06 01:30:13", updated_at: "2018-01-03 22:54:31",```
User.find_by_id(333)
D, [2018-01-09T01:53:31.652219 #127] DEBUG -- : User Load (1.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 333 LIMIT 1
=> nil
User.where(id: [333])
D, [2018-01-09T18:14:15.389575 #129] DEBUG -- : User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (333)
=> #<ActiveRecord::Relation []>
User Model:
(I omitted the methods in the model since they don't affect the behavior of fetching a record)
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, #:registerable,
:recoverable, :rememberable, :trackable, :validatable, :timeoutable
# has_many :posts, dependent: :destroy
has_many :cards, dependent: :destroy
has_many :card_templates, dependent: :destroy
has_many :provider_posts, class_name: "Post", foreign_key: "provider_id"
has_many :foods, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :messages, foreign_key: :sender_id, dependent: :destroy
has_many :received_messages, class_name: "Message", foreign_key: "chatroom_id"
has_many :notifications, dependent: :destroy
has_many :conversations, dependent: :destroy
has_many :chatrooms, through: :conversations
has_many :chat_messages, dependent: :destroy
has_many :chatusers
has_many :chats, through: :chatusers
# has_many :partnerships
has_many :providers, class_name: "Partnership", foreign_key: "user_id", dependent: :destroy
has_many :patients, class_name: "Partnership", foreign_key: "provider_id", dependent: :destroy
has_many :provider_users, through: :providers, source: :provider
has_many :patient_users, through: :patients, source: :patient
# has_many :schedulings
has_many :provider_schedulings, class_name: "Scheduling", foreign_key: "user_id", dependent: :destroy
has_many :patient_schedulings, class_name: "Scheduling", foreign_key: "provider_id", dependent: :destroy
has_many :scheduled_providers, through: :provider_schedulings, source: :provider
has_many :scheduled_patients, through: :patient_schedulings, source: :patient
has_many :mobile_devices, dependent: :destroy
has_many :hidden_messages, dependent: :destroy
has_many :star_categories, dependent: :destroy
has_many :starred_cards, through: :star_categories
has_many :user_programs, dependent: :destroy
belongs_to :profile_status
belongs_to :appointment_frequency
has_many :glucoses, dependent: :destroy
has_many :healthkits, dependent: :destroy
has_many :scales, dependent: :destroy
has_many :progress_images, dependent: :destroy
has_many :temp_food_images, dependent: :destroy
# accepts_nested_attributes_for :provider_users
# accepts_nested_attributes_for :providers
mount_uploader :avatar, AvatarUploader
mount_base64_uploader :avatar, AvatarUploader
mount_uploader :license, LicenseUploader
mount_base64_uploader :license, LicenseUploader
mount_uploader :insurance, InsuranceUploader
mount_base64_uploader :insurance, InsuranceUploader
mount_uploader :insurance2, InsuranceUploader2
mount_base64_uploader :insurance2, InsuranceUploader2
validates :email, presence: true, uniqueness: true
validates :phone, presence: true, uniqueness: true
validates :first_name, presence: true
validates :last_name, presence: true
before_create :set_default_timezone
before_create :clear_phone
before_create :create_auth_token
after_commit :sync_to_drchrono_and_salesforce, on: :create
after_commit :create_chatroom, on: :create
after_commit :create_default_cards, on: :create
after_commit :check_pending_appointments, on: :create
after_commit :connect_tech_provider, on: :create
after_update :check_pending_appointments, if: :email_changed?
after_update :start_drchrono_refresh_token_worker, if: :drchrono_access_token_changed?
after_update :start_fitbit_refresh_token_worker, if: :fitbit_access_token_changed?
after_update :sync_stats_to_salesforce
after_update :regenerate_progress_images, if: :last_seen_at_changed?
scope :patients, -> { where("provider != true and demo != true") }
scope :providers, -> { where(provider: true) }
scope :is_app, -> { where(app: true) }
end
Not sure why the following worked.
In the psql console I updated the email of user 333 and then it started to work. I was able to select it. I was also able to update the email to it's original value.
Maybe it is an issue with aptible db.
I have 2 models as below,
Updated based on suggestions
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
and
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company, :role]}
end
I find the uniqueness validation is working only on the update request. On create request validation is not functioning and it simply bypasses it.
I want to enable the same validation to reject if a user has same company & role assigned more than once.
If you want a ensure the uniqueness of user on unique pair of :company and :role, then you can try following. By default, the validations run for both create and update. You don't need :on => [ :create, :update ]. So it should be just:
validates :user, uniqueness: {scope: [:company, :role]}
Solved this issue with the below validation,
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
validate :company_users, :uniqueness_of_company_users
end
private
def uniqueness_of_company_users
errors.add(:company_users, 'error in role creation') if company_users.map{|x| "#{x.company_id} #{x.role_id}"}.uniq.size != company_users.size
end
This is the additional validation required to solve the issue
Thanks Rich Peck for this https://railscoding.wordpress.com/2015/04/27/uniqueness-gotcha/
From the docs
The :on option takes one of the values :create or :update
--
A validation is only run on create or update anyway, right?
find doesn't manipulate the db, destroy gets rid of the record & new just invokes a new instance of the object. You have literally zero other reasons to validate.
So, really, you should have:
validates :user, uniqueness: {scope: [:company_id, :role_id]}
This will look up against the values in company_id and role_id, which is probably going to be more efficient than calling the company and role objects themselves.
I could be wrong, but I really think if you used the above, it should work.
--
You may also wish to clean up your models:
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company_id, :role_id]}
end
How can add a cascade of deletes that will remove Profile, TodoList, and TodoItem rows for any User removed.
User Model:
class User < ActiveRecord::Base
has_one :profile
has_many :todo_lists
has_many :todo_items, through: :todo_lists, source: :todo_items
validates :username, presence: true
end
Profile Model:
class Profile < ActiveRecord::Base
belongs_to :user
validates :first_name, presence: true
validates :last_name, presence: true
validates :gender, inclusion: %w(male female)
validate :first_and_last
validate :male_Sue
def first_and_last
if (first_name.nil? and last_name.nil?)
errors.add(:base, "Specify a first or a last.")
end
end
def male_Sue
if (first_name == "Sue" and gender == "male")
errors.add(:base, "we are prevent male by name Sue.")
end
end
end
TodoList Model:
class TodoList < ActiveRecord::Base
belongs_to :user
has_many :todo_items, dependent: :destroy
default_scope { order :list_due_date }
end
TodoItem Model:
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
default_scope {order :due_date }
end
Thanks, Michael.
I guess adding dependent: :destroy will do.
#user.rb
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :todo_lists, dependent: :destroy
has_many :todo_items, through: :todo_lists, source: :todo_items, dependent: :destroy
validates :username, presence: true
end
From the docs:
has_many, has_one and belongs_to associations support the :dependent option. This allows you to specify that associated records should be deleted when the owner is deleted
By using dependent: :destroy on your association in the User class, anytime you destroy a User, all associated objects to that instance gets destroyed as well.
You can check this documentation for more information.