How to update has_many :through table? - ruby-on-rails

This is a follow up question from one of my earlier questions: How to Model doctor and patient relation
I am fairly new to rails, I am making an appointment booking system between patient and doctor, I have made the relationships, set up authentication using devise. I have 3 models:
class Doctor < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Patient < ApplicationRecord
has_many :appointments
has_many :doctors, through:appointments
end
class Appointment < ApplicationRecord
#table_columns: id | start_time| end_time| doctor_id| patient_id|slot_taken|
belongs_to :patient
belongs_to :doctor
end
I have created the appointments controller: Some actions are below:
#current_doctor comes from devise. Have created two separate models for doctor and patients using devise
def create
#appointment = current_doctor.appointments.build(appointment_params)
respond_to do |format|
if #appointment.save
format.html { redirect_to appointments_path, notice: 'Appointment was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #appointment.update(appointment_params)
format.html { redirect_to appointments_path, notice: 'Appointment was successfully updated.' }
else
format.html { render :edit }
end
end
end
private
def appointment_params
params.require(:meeting).permit(start_time, end_time)
end
As you can see, the doctor can create the time slots of when he is available, and can edit,update and destroy the same for him/herself and when he creates one the appointment table gets updated with doctor_id, start_time and end_time.
Now, what should I do to add the patient_id and change slot_taken to true (default: false) to it? When the patient books the available slot, the appointments table should be updated with patient_id and slot_taken value. How should I update the appointments table and where should I put the code for the same?

First of all, some advice: I don't think two user models it's a good idea. Think it would be best have one User devise model, and that user have an Role associated for it, so you control the access type by that. If you have to add more type of users to that system, creating one model for each one can be troubling.
About the appointments, question, in a roughly way, if you have an doctor instance, and you wanna make an appointment for some patient at s start_time and e end_time, you can do something like:
doctor.appointments.find_by(start_time: s, end_time: e).update(patient_id: your_patient_id)
About where to put that, depends on how your system works. Is the client that assigns himself? Or some administrator does that?
Anyway, think you should have another action/view for doing that assignment, so you can have a form where the user can input that information, and when he sends the form, you handle it and do the update in some way like a showed before.
Hope this helps, good luck

Related

Ruby on Rails - Missing Attribute Error as a result of association

I have two models.
Company
Memorandum
I want the company to have multiple memorandums and the memorandum to have only one company.
memorandum.rb
class Memorandum < ActiveRecord::Base
belongs_to :company
end
company.rb
class Company < ActiveRecord::Base
has_many :memorandums, dependent: :destroy
# validation lines omitted
end
When I try to assign the foreign key to the memorandum I get a Missing Attribute Error can't write unknown attribute "company_id"
I assign the company inside the companies controller. The memorandum is created prior to this and the id of the current memorandum is held inside the session hash.
companies_controller.rb
def create
#company = Company.new(company_params)
Memorandum.find(session[:memorandum_id]).company = #company
respond_to do |format|
if #company.save
format.html { redirect_to #company, notice: 'Company was successfully created.' }
format.json { render action: 'show', status: :created, location: #company }
else
format.html { render action: 'new' }
format.json { render json: #company.errors, status: :unprocessable_entity }
end
end
end
I strongly recommend you do read the Ruby on Rails Guides for Active Record Association. Concepts are very clearly explained there with example.
Here are two example links: has_many and belongs_to that would help you understand.
To fix your current problem, you need a migration to add company_id to your memorandums table:
rails g migration addCompanyIdToMemorandums company_id:integer
and, then run the migration:
bundle exec rake db:migrate
As the above guide will explain, you need a foreign key in the belongs_to association's table to point it's parent table. As, your memorandums belongs_to company, so you have to add company_id in your memorandums table which we just did using the above migration.

Update attributes of parent model?

I have been playing around but I cannot seem to figure out how to update the attributes of a parent model.
I have a model called Enhancement and a model called EnhancementComment associated with it. So enhancements can have comments.
I have this successfully working:
EnhancementComments Controller:
def create
#enhancement = Enhancement.friendly.find(params[:enhancement_id])
#enhancement_comment = #enhancement.enhancement_comments.create!(enhancement_comment_params)
respond_to do |format|
if #enhancement_comment.save
format.html { redirect_to #enhancement }
format.json { render action: 'show', status: :created, location: #enhancement_comment }
else
format.html { render action: 'new' }
format.json { render json: #enhancement_comment.errors, status: :unprocessable_entity }
end
end
end
What I want to do: When a comment is created, I want to update the attribute called :updated_at within the Enhancement model (not the EnhancementComment model).
How can I do this?
Thanks.
Updated
Here is what I have now:
class Enhancement < ActiveRecord::Base
# Associations
has_many :enhancement_comments, class_name: 'EnhancementComment', dependent: :destroy
class EnhancementComment < ActiveRecord::Base
# Associations
belongs_to :enhancement, touch: true
This alone doesn't seem to be working when I create a new comment.
Set the touch option to true on the child association. It's explicitly for what you're trying to do, update the updated_at of the opposite end of the association on save.
class Enhancement < ActiveRecord::Base
has_many :comments, class_name: 'EnhancementComment'
end
class EnhancementComment < ActiveRecord::Base
belongs_to :enhancement, touch :true
end
:touch
If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or destroyed. If you specify a symbol, that attribute will be updated with the current time in addition to the updated_at/on attribute.

Rails - can't tell create controller which item to create

I have several models:
User
Relationship
Project
Schedule
A user instance can be of type :student or :employer.
A schedule belongs_to a project and
a project has_one schedule
project belongs_to a student and a student has_many projects.
A relationship belongs_to Student and relationship also belongs_to Employer. Employer and Student both has_many relationships.
I am writing the controller to create a schedule and I can't figure out how to tell the controller which project the schedule belongs to. Here is what I have so far
def create
if current_user.type == 'Employer'
redirect_to employer_profile_path(current_user.profile_name)
else
#schedule = Schedule.find(params[:id])
if #schedule.save(schedule_params)
flash[:notice] = "Successfully created schedule."
redirect_to profile_path(current_user.profile_name) #change to project path later
else
render :action => 'new', :notice => 'Invalid Schedule'
end
end
end
private
def schedule_params
params.require(:schedule).permit(tasks_attributes: [:title, :content, :_destroy])
end
I am pretty sure I am defining #schedule incorrectly. The routes for schedule are:
resources :schedules
How do I tell the controller which Project the schedule belongs to?
Assuming you posted the create action for the SchedulesController there is no need for:
#schedule = Schedule.find(params[:id])
this will probably return nil, because there is no schedule (after all that is what you want to create here).
#schedule = Schedule.new(schedule_params)
if you want to assign a project to the schedule, you need to submit this information so you can access it in the params hash. Something like:
#schedule.project = Project.find(params[:project_id])

Restrict access to "destroy" feature in Ruby on Rails app

Thanks in advance for your help! I have a site built in Ruby on Rails (2.3.15, 1.8.7) that lets people create itineraries to different locations and with different activities at each location. The site has a way of ensuring that the current user can only delete the itineraries associated with his account. The code in itineraries_controller.rb to do this looks something like this:
def destroy
#itinerary = Itinerary.find(params[:id])
if #itinerary.user_id == current_user.id
#itinerary.destroy
respond_to do |format|
format.html { redirect_to('/home') }
format.xml { head :ok }
end
else
redirect_to '/'
end
end
This system works well. I want to do the same thing for the activities, however. In the activities_controller, I want to ensure that the current user can only delete the activities associated with his account.
Currently, here are my four models (with unnecessary stuff stripped out):
User.rb
class User < ActiveRecord::Base
has_many :itineraries
end
Itinerary.rb
class Itinerary < ActiveRecord::Base
has_many :locations
belongs_to :user
end
Location.rb
class Location < ActiveRecord::Base
belongs_to :itinerary
has_many :activities
end
Activity.rb
class Activity < ActiveRecord::Base
belongs_to :location
end
Here is where I'm starting with activities_controller.rb. FYI, my current_user.id is created by an authenticated_system.rb, so don't worry about that part.
def destroy
#activity = Activity.find(params[:id])
if #itinerary.user_id == current_user.id
#activity.destroy
respond_to do |format|
format.html { redirect_to("/") }
format.xml { head :ok }
end
else
redirect_to '/'
end
end
I tried to connect the models using a has_many :through approach, but I didn't know how to do it for more than three models, and I wasn't sure that was the right approach anyway. Thanks again for any help you can provide!
You can simply do
if #activity.location.itinerary.user_id == current_user.id
You really only need to ask the activity for its location, then ask the location for its itinerary, than ask it for its user. It's a bit of a violation of the Law of Demeter, but it will work.
loc = #activity.location
itin = loc.itinerary
if itin.user_id = current_user.id
...
That should work.

How to associate two foreign key to a post on create

I have 3 models as below and would like to associate two foreign key to a post on create (user_id (from User model) and review_id (from Review model) to Goal model. I manage to associate the user_id using 'current_user' to goals on create by using the solution given in the link below but not sure on how to go about getting this done as well for review_id.
Thanks.
Devise how to associate current user to post?
My models:
class User < ActiveRecord::Base
has_many :reviews
has_many :periods, :through => :reviews
has_many :goals
end
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :period
has_many :goals
end
class Goal < ActiveRecord::Base
belongs_to :user
belongs_to :review
end
My goals_controller.rb
def create
#goal = current_user.goals.build(params[:goal])
respond_to do |format|
if #goal.save
format.html { redirect_to #goal, notice: 'Goal was successfully created.' }
format.json { render json: #goal, status: :created, location: #goal }
else
format.html { render action: "new" }
format.json { render json: #goal.errors, status: :unprocessable_entity }
end
end
end
Cheers,
Azren
While I don't understand what the goal is. One general way I usually use is to model the relationships to something you are familiar with or you can easily find good examples. For example, think about your use case as user-question-answer models. Same as your use case, user has many questions and many answers. question has many answers and belongs to user, and answer belongs to user and question.
So things become easy, right? Let's see how stackoverflow implements this. You can check the html code of a comment box, and the form action is something like (/questions/10186415/answer/submit). Here 10186415 is the question id, it's passed to the server side, so when an answer is created, this question id can be used and related.
Back to your case, the goal form should know what review it's for. The review id could be a hidden field, or part of the submit url.

Resources