How it works - `belongs_to :user, dependent: :destroy` - ruby-on-rails

I know how to work has_many :posts, dependent: :destroy. If User or something that has_many posts is destroyed, all belonging posts are also destroyed.
But what happens when Post model belongs_to :user, dependent: :destroy?
I found the option in Rails Guide, but I couldn't find out how to work it.
http://guides.rubyonrails.org/association_basics.html

"has_many"
A teacher "has_many" students. Every student has only one teacher, but every teacher has many students. This means that there is a foreign key, or teacher_id on the student referencing to what teacher they belong to.
"belongs_to"
A student "belongs_to" a teacher. Every teacher has many students, but every student has only one teacher. Again, there is the foreign key on the student referencing to what teacher they belong.
Let's work this out an using this student / teacher concept.
Teacher Model
class Teacher < ActiveRecord::Base
has_many :students, dependent: :destroy
end
Student Model
class Student < ActiveRecord::Base
belongs_to :teacher
end
Assuming these models then
Teacher.destroy
Will delete the instantiated teacher and all the students that were associated to that teacher.
For example
Teacher.find(345).destroy
Would destroy the record of the teacher with the ID of 345 and destroy all the associated students with that teacher.
Now to the heart of the question, what happens when my models look like this?
Teacher Model
class Teacher < ActiveRecord::Base
has_many :students, dependent: :destroy
end
Student Model
class Student < ActiveRecord::Base
belongs_to :teacher, dependent: :destroy
end
If I were to call
Student.destroy
This would destroy the instantiated student and that student's associated teacher. However to my knowledge (and according to the docs) this would not destroy any other students related to that teacher, leaving them 'orphaned'.
Here is a quote from the Ruby docs on this 1
If set to :destroy, the associated object is destroyed when this object is. This option should not be specified when belongs_to is used in conjunction with a has_many relationship on another class because of the potential to leave orphaned records behind.

It instantiates an instance of all of the users then sends the message destroy to each. Results in normal destruction lifecycle for the users that are destroyed this way.

I have try set dependent: :destroy into belongs_to. Example
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
end
class Comment < ActiveRecord::Base
belongs_to :post, dependent: :destroy
end
In a console, if comment has been destroy, post will be destroy
But I think you shouldn't set belongs_to + dependent: :destroy together
In fact, example on facebook, when 1 comment of 1 post has been deleted, this post will not be deleted

Related

How to delete all the associations of a model when deleting it in Rails 5

I have 3 models in my project. User, Meal & Food.
A user has many meals. A meal can have many food items and a food item can be a part of many meals.
The user and the meal model are in a has_many association, while the meal and the food model are in a has_many :through association. The join model for the meal and the food model is called MealFood.
When deleting the user I have made it so that it deletes all of the meals that belong to the user. However I can't make it so that it also deletes all the associations of the meal that belong to the user.
I need to delete every record in the meal_foods table where the meal_id belongs to the user that is being deleted.
User Model
class User < ApplicationRecord
has_many :meals, :dependent => :delete_all
end
Meal Model
class Meal < ApplicationRecord
belongs_to :user, optional: true
has_many :meal_foods, :dependent => :delete_all
has_many :foods, through: :meal_foods
end
Food Model
class Food < ApplicationRecord
has_many :meal_foods
has_many :meals, through: :meal_foods
end
MealFood Model
class MealFood < ApplicationRecord
belongs_to :meal
belongs_to :food
end
Thanks in advance!
You probably want dependent: :destroy, not dependent: :delete_all. :delete_all won't run callbacks and that's likely why your deeper associations remain persisted.
See the docs here:
For has_many, destroy and destroy_all will always call the destroy
method of the record(s) being removed so that callbacks are run.
However delete and delete_all will either do the deletion according to
the strategy specified by the :dependent option, or if no :dependent
option is given, then it will follow the default strategy. The default
strategy is to do nothing (leave the foreign keys with the parent ids
set), except for has_many :through, where the default strategy is
delete_all (delete the join records, without running their callbacks).
This thread has better answers.

Association for model - has_many :through

I have three models - Company, User and CompanyUser. The associations are as follows.
Company.rb
has_many :company_users
has_many :users, :through => :company_users
User.rb
has_many :company_users, :dependent => :destroy
belongs_to :company
CompanyUser.rb
belongs_to :company
belongs_to :user
For fetching current_user.company, what moddifications are to be made in the model association?
Any help would be appreciated.
It should be:
has_many :companies, through: :company_users
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model.
So if you are creating three models and making a has_many :through association I believe that User will have many Companies and Company will have many Users.
But if you need that the user belongs to only one company instead of creating the third model save the company_id in the users table itself.
Update:
Now as your scenario is A company can have may users and User belongs to a single company, you need two models: User and Company. Your User model should have an attribute company_id and then company_id should be saved in users table only. Then the associations as follows:
class User < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :users
end
Then you can do current_user.company
You can get more information on associations in the RailsGuides
According to the associations you have taken,
user already have as association with the company through the Company User model, so user may have many companies according to your associations.
so,
class User < ActiveRecord::Base
has_many :company_users, :dependent => :destroy
has_many :companies, :through => :company_users
end
current_user.companies will give you the companies.
But if you need only one company for a user then,
class User < ActiveRecord::Base
belongs_to :company
end
take belongs_to company and save company_id in users table,
then you can call,
`current_user.company`
According to your logic,
I think you may need to create a new variable session current_company_user which is object CompanyUser.
And then, to fetch company by :
current_company_user.company

Ruby on Rails has_many :through in a polymorphic association

I have searched and searched and found only partial solutions to my current question.
The thing is, I'd like to know if it is possible to use has_many :through along with a polymorphic association in Ruby on Rails.
I have a system where students can create travel plans (that can belong to many students) and refund claims (that can belong to only one student) for their projects. In this system, both admin users and students are able to comment on the plans and claims.
My associations are:
class Student < ActiveRecord::Base
has_and_belongs_to_many :travel_plans
has_many :refund_claims
has_many :comments, through: :travel_plans
has_many :comments, through: :refund_claims
end
class AdminUser < ActiveRecord::Base
has_many :comments
end
class TravelPlan < ActiveRecord::Base
has_and_belongs_to_many :students
has_many :comments, as: :commentable
end
class RefundClaim < ActiveRecord::Base
belongs_to :student
has_many :comments, as: :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
My questions are:
Is it correct to associate comments twice in the Student model?
I don't want the AdminUsers to have travel plans and refund claims, how can I identify their comments as being made on a travel plan or on a refund claim?
Would there be a better approach?
Thanks a lot in advance for everyone!
Cheers,
You probably want to add an polymorphic author attribute to the Comment model. Than you just need has_many :comments, as: :author to the Student and AdminUser model.
If this is a new application and you are starting on the green field you might want to rethink your models a bit and add a Role and a User model. Student would than be a role of user as would AdminUser be.
Is it correct to associate comments twice in the Student model?
No, not really. If you have duplicate association name, you can only use one of them. If you want to use both, you have to name them differently.

Planning a Rails application associations

I'm in the process of trying to develop my first rails application and before I jump off into actually implementing my ideas, I was hoping I could get some help with my association planning.
My application is going to be an educational tool and I have some basic functionality that I need to implement. I want to have students that can register for and create courses. Within these courses, there will be one user who is the teacher and the rest are students. Within the course will be assignments that the teacher creates and that users are required to make a submission for. These are the basics. Eventually I want to add more functionality but as of now I just need the foundations set.
Here are my ideas so far on what the associations should look like:
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
However, I'm running into my wall of inexperience at the moment with how to appropriately handle the rest of the associations at this point. I could probably hack out something, but I'd prefer to do it as best as I can the first time.
How do I handle the associations related to assignments and submissions? Presumably a student's submission should belong_to them, but it is also specifically related to an assignment within the class. Assignments originate within a course, but are also closely tied to the user.
Finally, what's the best way to handle the relationships between a user and the class they create? A user creates a course. Does that course belong_to them? Should I just add a column to the course's table for storing the creator's id? Etc.
Thanks for the help.
Suggestion:
You might want to separate out your Teacher and Student models, since you're very likely to have different actions associated with each (and while they share some attributes, they really are different entities in your model, for example, you likely want just one teacher teaching in a course.)
You could derive both the Teacher model and the Student model from a User model that has the shared attributes and authentication.
Now to your questions:
If you'd like to keep the student that created the course associated, creator_id is the way I'd go. (If a teacher can create a course too, deriving Student and Teacher from a shared User model would help)
Assignments and Submissions:
You've pretty much defined it in words. Here is the code.
[If different students within a course could get different assignments, only then do you want to build a direct association between Student and Assignment, otherwise, use a through association]
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
has_many :assignments, :through => :courses
has_many :submissions
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
has_many :assignments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
class Assignment < ActiveRecord::Base
belongs_to :course
has_many :submissions
end
class Submission < ActiveRecord::Base
belongs_to :assignment
belongs_to :user
end

has_many through association dependent destroy under condition of who called destroy

Is there a way to check, within a before_destroy hook, what object (class) called destroy?
In the following example, when a patient is destroyed, so are their appointments (which is what I want); however I don't want to allow a physician to be destroyed if there are any appointments associated with that physician.
Again, is there a way to do such a check in the before_destory callback? If not, is there any other way to accomplish this "destruction check" based on the "direction" of the call (i.e. based on who called)?
class Physician < ActiveRecord::Base
has_many :appointments, dependent: :destroy
has_many :patients, through: :appointments
end
class Patient < ActiveRecord::Base
has_many :appointments, dependent: :destroy
has_many :physicians, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :patient
belongs_to :physician
before_destroy :ensure_not_referenced_by_anything_important
private
def ensure_not_referenced_by_anything_important
unless patients.empty?
errors.add(:base, 'This physician cannot be deleted because appointments exist.')
false
end
end
end
Note that dependent: :destroy on a has_many :through relationship only deletes the association and not the associated record (i.e. the join records will be deleted, but the associated records won't). So if you delete a patient it will only delete the appointment and not the physician. Read the detailed explanation in the API docs.
I have pasted the relevant paragraphs below.
What gets deleted?
There is a potential pitfall here: has_and_belongs_to_many and has_many :through associations have records in join tables, as well as the associated records. So when we call one of these deletion methods, what exactly should be deleted?
The answer is that it is assumed that deletion on an association is about removing the link between the owner and the associated object(s), rather than necessarily the associated objects themselves. So with has_and_belongs_to_many and has_many :through, the join records will be deleted, but the associated records won’t.
This makes sense if you think about it: if you were to call post.tags.delete(Tag.find_by_name('food')) you would want the food tag to be unlinked from the post, rather than for the tag itself to be removed from the database.
Just say:
class Physician < ActiveRecord::Base
has_many :appointments, dependent: :restrict_with_exception
has_many :patients, through: :appointments
end
Note the dependent: :restrict_with_exception. This will cause Active Record to refuse to destroy any Physician records that have associated Appointment records.
See the API docs and the association basics guide.

Resources