I am trying to run a script to delete a whole bunch of students from our system, I am relying on the rails dependent: :destroy convention to make sure I clean up all data that's is related to those students.
I am pretty new to this system but this is how they structured the has_many relationships within the student_application model which belongs to a student.
student.rb student model
has_many :applications, class_name: "StudentApplication", dependent: :destroy
has_many :season_classes, through: :applications
has_many :payments, foreign_key: "student_id", dependent: :destroy
student_application.rb student_application model
belongs_to :student, touch: true
has_many :user_application_statuses, -> { order(id: :asc) }, dependent: :destroy
has_many :user_application_tasks, through: :user_application_statuses
has_many :file_upload_tasks, through: :user_application_statuses, class_name: "Tasks::FileUploadTask", source: :user_application_tasks
has_many :payment_tasks, through: :user_application_statuses, class_name: "Tasks::PaymentTask", source: :user_application_tasks
has_many :payments, through: :payment_tasks
user_application_status.rb user_applicaton_status model
belongs_to :application_status
# Student links
belongs_to :student_application
has_one :student, through: :student_application
payment.rb payment model
belongs_to :student
has_one :payment_task, class_name: "Tasks::PaymentTask"
has_many :transactions
When I delete a user I am getting this error
PG::ForeignKeyViolation: ERROR: update or delete on table "student_applications" violates foreign key constraint "payments_student_application_id_fkey" on table "payments"
DETAIL: Key (id)=(24747) is still referenced from table "payments".
: DELETE FROM "student_applications" WHERE "student_applications"."id" = $1
(0.3ms) ROLLBACK
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "student_applications" violates foreign key constraint "payments_student_application_id_fkey" on table "payments"
DETAIL: Key (id)=(24747) is still referenced from table "payments".
: DELETE FROM "student_applications" WHERE "student_applications"."id" = $1
At first I thought that there was an object at a deeper relationship level that was being left out. But as far as I can tell from looking at the tables and the source code there is no payments_student_application_id_fkey reference anywhere in the code, but i have found this in the structure.sql file
--
-- Name: payments_student_application_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY payments
ADD CONSTRAINT payments_student_application_id_fkey FOREIGN KEY (student_application_id) REFERENCES student_applications(id);
We are using Rails 4.1.14.1 and Ruby 2.1.6 and Postgres for the db. Any ideas as to what could be causing this issue?
From What I can see...
# in pseudocodes
user has_one or has_many student_application dependent destroy
student_application belongs to a user
student_application has_many payments
Therefore deleting a user causes the associated student_application to get deleted as well... but the student_application's id is being referenced from the payments table, therefore causing the error.
Two ready solutions I can see:
1) Set a dependent: :destroy on the student_application model for payments ( or payment_tasks ) as well. This will ensure that the payments also gets deleted as well.
However, if you don't want this to be the case... then option 2:
2) Set a dependent: :nullify on the student_application model for payments. This will ensure that the ``student_application_idcolumn on the associatedpaymentsobject to the deletedstudent_application` is set to null, preventing the above error.
:dependent Controls what happens to the associated objects when the associated parent is destroyed. More options for dependent can be found here.
Related
I have 3 relevant tables/models. I would like to retrieve the user which has organized a party, given a Party record, a join table with a boolean organized column, and a users table.
My best attempt so far (this one makes the most sense to me after much fiddling). I've omitted the irrelevant columns.
class Party
# party_id
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :organizer,
-> { where organizer: true },
through: :parties_users,
source: :user
class PartiesUser
# party_id
# user_id
# organized:bool
belongs_to :party
belongs_to :user
class User
# user_id
has_many : parties_users
The above setup raises the following error, which I honestly don't fully understand:
ActiveRecord::HasOneThroughCantAssociateThroughCollection (Cannot have a has_one :through association 'Party#organizer' where the :through association 'Party#parties_users' is a collection. Specify a has_one or belongs_to association in the :through option instead.)
I know I can do this via an instance method, but given the frequency types of use, my app would massively benefit from having this as an association.
As the error message says, you can't have a has_one through a has_many.
You could (instead) do it this way if the organizer flag is on the join record...
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :main_party_user, -> {where organizer: true}, class_name: 'PartiesUser'
has_one :organizer, through: :main_party_user, class_name: 'User'
I want to select all Notices that belong to the Character via the has_one association that have a nil supernotice. How do I code this?
notice.rb:
belongs_to :character
has_one :active_comment_relationship, class_name: "Commentrelationship",
foreign_key: "commenter_id",
dependent: :destroy
has_one :supernotice, through: :active_comment_relationship,
class_name: "Notice",
source: :commentee
accepts_nested_attributes_for :active_comment_relationship
has_many :passive_comment_relationships, class_name: "Commentrelationship",
foreign_key: "commentee_id",
dependent: :destroy
has_many :comments, through: :passive_comment_relationships,
class_name: "Notice",
source: :commenter
character.rb:
has_many :notices
def topNotices
self.notices.where(supernotice: nil) # doesn't work
end
Logs:
: SELECT "notices".* FROM "notices" WHERE "notices"."character_id" = $1 AND "notices"."commentee_id" IS NULL ORDER BY "notices"."created_at" DESC
Completed 500 Internal Server Error in 316ms (ActiveRecord: 12.0ms)
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column notices.commentee_id does not exist
LINE 1: ..."notices" WHERE "notices"."character_id" = $1 AND "notices"....
The logs show the error notices.commentee_id does not exist, but I've clearly stated in notice.rb that a notice has_one :supernotice through: :active_comment_relationship. Where am I going wrong?
You need to add:
has_many :notices
In your character model.
You have a problem in your association in the first place.
When you define associations between two models, you have to set up properly in both of the models.
In your notice.rb Model, you have belongs_to :character association, so you have to define the counterpart of this association in your character.rb Model as well. You have to define a has_many or has_one association in your Character model which in your case is has_many notices.
So, in your character.rb Model, you need to define this association:
has_many :notices
When you setup the associations in both the models properly, then you can expect to get the correct result using:
self.notices.where(supernotice: nil)
because now it knows how your Character model is associated with your Notice model.
I highly recommend you to read the Active Record Associations Guide
I have three models: Book, genre, BookGenre, and here are relationships:
class BookGenre < ActiveRecord::Base
belongs_to :book
belongs_to :genre
end
class Book < ActiveRecord::Base
has_many :book_genres
has_many :genres, through: :book_genres
end
class Genre < ActiveRecord::Base
has_many :book_genres
has_many :books, through: :book_genres
end
And then I use seed file to put data into these tables.
But when I want to do rake db:seed again, it showed this error
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "books" violates foreign key constraint "fk_rails_4a117802d7" on table "book_genres"
DETAIL: Key (id)=(10) is still referenced from table "book_genres".
In my seed.rb
Book.destroy_all
Genre.destroy_all
...create data
Add dependent: :destroy option to your has_many definitions.
Check docs
Yet better option to respect data integrity is to set the CASCADE DELETE on the database level: say, you have comments table and users table. User has many comments You want to add a foreign_key to table comments and set deleting the comment whenever the user is destroyed you would go with the following (the on_delete: :cascade option will ensure it):
add_foreign_key(
:comments,
:users,
column:
:user_id,
on_delete: :cascade
)
Try this:
ActiveRecord::Base.connection.disable_referential_integrity do
Book.destroy_all
Genre.destroy_all
# ...create data
end
I have a permissions model that is based on an individual Store. A store has a member that can view data has_and_belongs_to_many_users, edit data has_and_belongs_to_many_editors and own the Store has_and_belongs_to_many_owners.
If I were to add more permissions in the future, I would have to add more joins. I'm not sure I like this model.
Store has_and_belongs_to_many_users
Store has_and_belongs_to_many_owners -> [Users]
Store has_and_belongs_to_many_editors -> [Users]
An alternative is this, I factor out the role, and create a second join table called "authorized users".
Here is what that could look like:
Store has_and_belongs_to_many_authorized_users
authorized_users: User_id Role_id
How can I use a second join table, that is called "authorized_users" with rails? Looking at my model, I do not have a model called authorized_users, and I am not sure how to build a model that relates to a join table.
essentially my question is, with Rails, How can I join a join table with a join table?
Just answering your question as it is stated, I would prefer to put the roles in the join table myself
class User < ActiveRecord::Base
has_many :store_users, inverse_of: :user, dependent: :destroy
has_many :stores, through: :store_users
end
class Store < ActiveRecord::Base
has_many :store_users, inverse_of: :store, dependent: :destroy
has_many :users, through: :store_users
end
class StoreUser < ActiveRecord::Base
belongs_to :store
belongs_to :user
has_many :permissions, dependent: :destroy, inverse_of: :store_user
validates_presence_of :store, :user
end
class Permission < ActiveRecord::Base
belongs_to :store_user
validates_presence_of :role
end
If you need a join table, that can or should be a model, you want to use has_many :trough association. You can read more about it here: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
I have 2 models as describes below.
class EmpGroup < ActiveRecord::Base
belongs_to :user
has_many :emp_group_members, dependent: :destroy
end
and
class EmpGroupMember < ActiveRecord::Base
belongs_to :emp_group
belongs_to :user
end
now the problem is whenever I tried to destroy a group then I received a error as below.
PG::ForeignKeyViolation: ERROR: update or delete on table "emp_groups" violates foreign key constraint "fk_rails_bd68440021" on table "emp_group_members"
DETAIL: Key (id)=(1) is still referenced from table "emp_group_members".
What I'm missing?
Add cascading delete to your EmpGroup model:
class EmpGroup < ActiveRecord::Base
has_many :emp_group_members, dependent: :delete_all
end
Or
Are you calling delete method? you should call destroy instead.
Use .destroy
:dependent is one of the options available in belongs_to association
If you set the :dependent option to:
:destroy, when the object is destroyed, destroy will be called on its associated objects.
:delete, when the object is destroyed, all its associated objects will be deleted directly from the database without calling their destroy method.
Additionally, objects will be destroyed if they're associated with dependent: :destroy, and deleted if they're associated with dependent: :delete_all.
in has_many associations:
:destroy causes all the associated objects to also be destroyed
:delete_all causes all the associated objects to be deleted directly from the database (so callbacks will not execute)
you can try
emp_member_1= #emp_group.emp_group_members.first
##delete associated record
#emp_group.emp_group_members.delete(emp_member_1)
New syntax:
class EmpGroup < ActiveRecord::Base
has_many :emp_group_members, dependent: :destroy
end
When you delete a group, are you using delete or destroy. - I've had this error before, and it was because I had a typo and was using .delete instead of .destroy
Insert relation like this
has_many :children, :dependent => :destroy
To learn more about destroy and delete, go to this link
https://medium.com/#wkhearn/delete-vs-destroy-does-it-even-matter-8cb4db6aa660