I have probably one simple question. I have the following three tables:
create_table "appointments", force: true do |t|
t.integer "teacher_id"
t.integer "student_id"
t.datetime "start_date"
end
create_table "teachers", force: true do |t|
t.string "name"
end
create_table "students", force: true do |t|
t.string "email"
t.string "first_name"
t.string "last_name"
end
I have made the following models for these three type of data:
class Appointment < ActiveRecord::Base
belongs_to :teacher
belongs_to :student
end
class Teacher < ActiveRecord::Base
has_many :appointments
has_many :students, :through => :appointments
end
class Student < ActiveRecord::Base
has_many :appointments
has_many :teachers, :through => :appointments
end
So, every teacher can have many appointments with many different students and every student can have many appointments with different teachers. My question is how to delete an appointments which belong to some student if administrator deletes that student from database?
I thought I modeled this correctly but when I delete user like this, his appointments are still there:
def destroy
Student.find(params[:id]).destroy
end
Thank you!
class Student < ActiveRecord::Base
has_many :appointments, :dependent => :delete_all
has_many :teachers, :through => :appointments
end
delete_all will not call any destroy callbacks defined on appointment. but it is efficient because issues a single delete query
Related
I have tables in db as following migration script:
class CreateAppointments < ActiveRecord::Migration
def change
create_table :physicians do |t|
t.string :name
t.timestamps null: false
end
create_table :patients do |t|
t.string :name
t.timestamps null: false
end
create_table :appointments do |t|
t.belongs_to :physician, index: true
t.belongs_to :patient, index: true
t.datetime :appointment_date
t.timestamps null: false
end
end
end
And the model class looks like:
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, through: :appointments
end
I'm able to pick all patients data of specific physician using:
patients = Physician.find(123).patients
The problem is, I need appointment_date, which is stored in appointment table, along with patient details. Somehow I'm not able to figure out how to fetch it.
Relation looks completely right so querying should be quite easy or are you having some problems?
If you need appointment_date i would probably query Appointments of Physician instead:
Physician.find_by(id: 123).appointments.each do |x|
print "Date: #{x.appointment_date}\n"
print "Patient: #{x.patient.name}"
end
You can join Patient and Appointment models to get the required data easily:
Patient.joins(:appointments).where(. . . ).select('appointments.appointment_date')
You can put the required condition in the where clause and add more attributes from the patients and appointments tables in the select clause that you want to select.
See Active Record Query Interface for more information on this.
Update
I misunderstood your question initially. Th easiet way to get what you want is pointed by midN in his answer.
I have some issues conceptualizing my model: A User has 1:N appointements with other an other User. So I created a table Appointements and Users But for the join tables I end up having 2 user_id in the index this is wrong.
class AppointmentsUsersJointTable < ActiveRecord::Migration
def change
create_table :appointments_users, id: false do |t|
t.integer :user_id
t.integer :user_id
t.integer :appointment_id
end
add_index :appointments_users, :user_id
add_index :appointments_users, :user_id
add_index :appointments_users, :appointment_id
end
end
#app/model/user.rb
has_and_belongs_to_many :users
Other details: A User can have up to two roles (Rolify) it's either a :students or :mentor. So a User that has the role (students) wants to take a courses with a User that has the role :mentor
You have the right idea, but you need to name your columns different things. Perhaps student_id and mentor_id.
class AppointmentsUsersJoinTable < ActiveRecord::Migration
def change
create_table :appointments_users, id: false do |t|
t.integer :student_id
t.integer :mentor_id
t.integer :appointment_id
end
add_index :appointments_users, :student_id
add_index :appointments_users, :mentor_id
add_index :appointments_users, :appointment_id
end
end
You'll need to specify those custom column names using the foreign_key option when you declare the association in your User class.
This setup will work only so long as your appointments involve two people. If there can be more than two, or if for your courses there should be one mentor and an arbitrary number of students, you'll need to set it up differently. It will probably also be beneficial to make Mentor and Student subclasses of User so you can distinguish them better. Here's one possible skeleton for that:
class User; end
class Mentor < User
has_many :courses
has_many :students, through: :courses
end
class Student < User
has_many :enrollments
has_many :courses, through: :enrollments
has_many :mentors, through: :courses
end
class Course
belongs_to :mentor
has_many :enrollments
has_many :students, through: :enrollments
end
class Enrollment
belongs_to :course
belongs_to :student
end
New to rails (using 4.1), making a small project management tool as a learning project. I have run into a slightly more complex model association, and I want to make sure I am on the right track here, and ask how to extend it a bit.
Consider this situation:
Models:
class Website < ActiveRecord::Base
has_many :website_user
has_many :users, through: :website_user
has_many :tasks, through: :website_user
end
class User < ActiveRecord::Base
has_many :websites, through: :website_user
has_many :website_user
end
class WebsiteUser < ActiveRecord::Base
belongs_to :website
belongs_to :user
belongs_to :role
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :website_user
has_one :website, through: :website_user
end
class Role < ActiveRecord::Base
has_many :website_user
end
DB:
create_table "roles", force: true do |t|
t.string "name"
end
create_table "tasks", force: true do |t|
t.text "description"
t.string "title"
t.integer "website_user_id"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.string "password"
t.string "password_hash"
t.string "password_salt"
end
create_table "website_users", force: true do |t|
t.integer "website_id"
t.integer "user_id"
t.integer "role_id"
end
create_table "websites", force: true do |t|
t.string "name"
t.string "url"
end
What I have going on here is basically Websites get users (team members working on sites) associated though the website_user table. That table belongs to roles, so that a team member would have a specific job on this website, and finally, tasks belong to the website_user association, so that you could swap out a user, but the task would stay associated with the role and website.
I am looking into extending it more, so that the task would be associated on website_user twice, once for the assigner, once for the assigned user of the task. However, at this point, it feels like I will have an awful lot of things attached to a big join table in the middle, and without a ton of experience under my belt, it is starting to smell like there might be a better way.
If this all looks good, how would you join the tasks to the website_user twice, once for assigner, once for assigned? Or alternatively, how would rearrange the model association?
A simple solution that first comes to head is to keep assigner and assignee ids in Task model.
Migration AddAssigneeAssignerToTask
class AddAssigneeAssignerToTask < ActiveRecord::Migration
change do
add_reference :task, :assignee, index: true
add_reference :task, :assigner, index: true
end
end
Adding belongs_to into Task model
class Task < ActiveRecord::Base
belongs_to :assignee, class: 'WebsiteUser'
belongs_to :assigner, class: 'WebsiteUser'
has_one :website, through: :assignee
end
Modifying WebsiteUser
class WebsiteUser < ActiveRecord::Base
belongs_to :website
belongs_to :user
belongs_to :role
has_many :assigned_tasks, class_name: 'Task', foreign_key: 'assigner_id'
has_many :received_tasks, class_name: 'Task', foreign_key: 'assignee_id'
end
So afterwards you can use it like this
#website_user.assigned_tasks # => []
#website_user.received_tasks # => [Task1, Task2]
BUT
If you think to add some different functionality to either assigner or assignee, you should consider to use STI or MTI
class Task < ActiveRecord::Base
belongs_to :assignee, class_name: WebsiteUser, foreign_key:website_user_id
belongs_to :assigner, class_name: WebsiteUser, foreign_key:assigner_user_id
end
class WebsiteUser < ActiveRecord::Base
has_many :assigned_tasks, class_name: Task, inverse_of: :assignee, dependent: :destroy, foreign_key: :website_user_id
has_many :tasks_assigned, class_name: Task, inverse_of: assigner, dependent: :destroy, foreign_key: :assigned_user_id
end
You will have to add another foreign key in your tasks table..
just a starting point but this should get you going..
I can not advice you in the database design, but you can assign users twice using an option called class_name. You can read more here: http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference
But you will have to add additional foreign_key to your Tasks model as well.
And I also advice you to read following chapter of M. Hartle book, as it have really good explanation between relationships of models: https://www.railstutorial.org/book/following_users#cha-following_users
I'm trying to create a many to many relationship between two models in Rails 3.2.11.
A User can be associated with many Incidents and vice versa.
class User < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participant_id"
has_many :participated_incidents, through: :incident_participants
end
class Incident < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participated_incident_id"
has_many :participants, through: :incident_participants
end
The join table:
class IncidentParticipant < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
t.belongs_to :participant, class_name: "User"
t.belongs_to :participated_incident, class_name: "Incident"
end
Table for IncidentParticipants
create_table "incident_participants", :force => true do |t|
t.integer "participant_id"
t.integer "participated_incident_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
So, why doesn't rails get this relationship? When I try to do #incident.participants in my view I get this error:
"Could not find the source association(s) :participant or
:participants in model IncidentParticipant. Try 'has_many
:participants, :through => :incident_participants, :source => '.
Is it one of ?"
Any ideas?
Try taking out the t.belongs_to and replace with belongs_to.
To create a many to many association you should consider creating an association table. That is to say you will have two 1-M relationships that point to a sort interim table. For instance:
In your first model:
class Example < ActiveRecord::Base
has_and_belongs_to_many :example2
end
In your second model:
class Example2 < ActiveRecord::Base
has_and_belongs_to_many :example
end
Then you need to write a migration to link the two tables together:
class CreateTableExamplesExamples2 < ActiveRecord::Migration
create_table :examples_examples2 do |t|
t.integer :example_id
t.integer :example2_id
end
end
Then just let rails magic work. Check out the guides for more information.
I am linking a Company and User model through an Association table:
class User < ActiveRecord::Base
has_many :associations
has_many :companies, :through => :associations
class Company < ActiveRecord::Base
has_many :associations
has_many :users, :through => :associations
In my Association I have an boolean column named active:
create_table "associations", :id => false, :force => true do |t|
t.integer "company_id"
t.integer "user_id"
t.boolean "active"
In my company controller I am trying to get a company array that includes the fact that the user has been activated or not for the company.
I managed to use a scope, but this only achieved to filter if the company is active or not, and not the association:
#companiesactive = #user.companies.by_status(true)
Looking to get the result of something like:
#companiesuseractive = #user.companies.where("association.active", true)
Any help would be much appreciated.