Delete only join record between tables in Rails 4 - mysql

I have 3 Rails models as:
class User < ActiveRecord::Base
has_and_belongs_to_many :char_factors
end
class CharFactor < ActiveRecord::Base
has_and_belongs_to_many :users
end
class UserCharFact < ActiveRecord::Base
belongs_to :user
belongs_to :char_factor
end
In the above User and CharFactor models are joined through the UserCharFact model.
I'm creating new relations as:
def create
#user_character = UserCharFact.create({:user_id => #user.id, :char_factor_id => factor_id.id})
end
And the above seems to be working properly. But I can't find a way to delete a specific join relation between 2 tables. I tried the following:
def destroy
#user_character = CharFactor.find(params[:id])
#user.char_factors.delete(#user_character)
end
But it actually deletes the value from CharFactor table rather than just deleting the association

UserCharFact.where(char_factor_id: params[:id], user_id: #user.id).destroy_all

You delete it just like you delete any other model records.
user_char_factor = UserCharFactor.find_by(user_id: user_id, char_factor_id: char_factor_id)
user_char_factor.destroy if user_char_factor

Related

Updating the values on Many-to-Many relationship table

I'm facing an issue on the many to many relationship table using ruby on rails.
I have two tables and a relationship table(which is a joining table). They are named as follow
User
Role
Role_User
My problem is how do i update the values of user roles on the 3rd table.
Example
If I assign a
user_id 34 to admin(role_id = 2)
user_id 34 to supervisor(role_id = 3)
I should have two entries on Role_User table. Now I would like to update user_id 34 to admin(role_id = 1) How do I do this on rails
I have updating the user details with update_all command on rails like below
#user = User.where(id: user_params[:id]);
#user.update_all(
first_name: user_params[:first_name],
last_name: user_params[:last_name],
email: user_params[:email],
contact_number: user_params[:contact_number],
user_name: user_params[:user_name],
password: user_params[:password]
);
I try to update the role of users using the following code but it fails
#roles = user_params[:roles];
for role in #roles
#user.roles << Role.find(role)
end
#user.save;
You should be able to do something like that:
role = Role.find(ID)
user.roles << role
user.save
If you set up a many_to_many connection through rails, like below for example, then you don't have to touch the relational table. As nifCody said, you simply have to tell rails that you're appending to the user's roles and (as long as you have the proper associations) rails will automatically update the middle table for you.
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
end
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
class Role < ApplicationRecord
has_many :user_roles
has_many :users, through: :user_roles
end
But essentially, whatever is in the table is what rails sees. So if you already have data in the tables, you can leave it and continue with the method nifCody suggested. It is simply a different way of modifying the data not accessing it.
I have found a way to doing the updates on the third relationship table as following code,
First I delete all role_id for the particular user_id
Then insert the roles as new to the particular user_id
Here is the code I have written
#user = User.find(user_params[:id])
#roles = #user.roles.all
#roles = #user.roles.all
for role in #roles
if role
#user.roles.delete(role)
end
end
#user = User.find(user_params[:id])
#roles = user_params[:roles];
for role in #roles
#user.roles << Role.find(role)
end
if #roles
#user.save
end

Rails has many :through relationship causing stack level too deep

class User < ActiveRecord::Base
has_many :case_users
has_many :cases, :through => :case_users
end
class CaseUser < ActiveRecord::Base
belongs_to :case
belongs_to :user
end
class Case < ActiveRecord::Base
has_many :case_users
has_many :users, :through => :case_users
end
When I try to hit any users or cases endpoint, it continually sends queries to the DB like:
SELECT `cases`.* FROM `cases` INNER JOIN `case_users` ON `cases`.`id` = `case_users`.`case_id` WHERE `cases`.`deleted_at` IS NULL AND `case_users`.`deleted_at` IS NULL AND `case_users`.`user_id` = 1 [["user_id", 1]]
and like:
SELECT `users`.* FROM `users` INNER JOIN `case_users` ON `users`.`id` = `case_users`.`user_id` WHERE `users`.`deleted_at` IS NULL AND `case_users`.`deleted_at` IS NULL AND `case_users`.`case_id` = 1 [["case_id", 1]]
Why is this happening?
Edit:
These models are actually much larger (80-100 lines), but I've commented bits out and believe this is what's causing the problem/error. Also it's running these queries when crashing, which leads me to believe that it's coming from this relationship.
After spending ~1 hour looking at my models, my database schema, and testing all over, I found it was a problem in my serializer.
I had has_many :users in my serializer for cases, and that caused the error.

Rails Active Record, get related record from has_many :through relationship with where clause on through record

I have a relationship set up with a has_many :through.
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
# physician_id, patient_id
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, through: :appointments
end
How can I get all patients for a given physician where the appointment's role is equal to PI or G2?
I've tried Physician.find(50).appointments.where('role = ? or role = ?', 'PI', 'G2').patients
Edit:
I'm getting undefined method from the above. Shouldn't I be able to get the through's related records? In Physician.find(50).appointments.where('role = ? or role = ?', 'PI', 'G2') there should be an appointments method but there is not.
Since you want Patient objects back, start with that model. You want to add WHERE clauses on both Appointments and Physicians, so join on those associations. Use the Hash form of where to reference the joined tables.
Patient.joins(:physician).
joins(:appointments).
where(appointments: {role: ["PI", "G2"]}).
where(physicians: {id: physician_id}).uniq
Update
Consider adding scopes to your models that you can reuse:
class Patient < ActiveRecord::Base
scope :for_physician, ->(physician_id) do
joins(:physicians).
where(physicians: {id: physician_id}
end
scope :for_roles, ->(roles) do
joins(:appointments).
merge(Appointment.for_roles(roles))
end
end
class Appointment < ActiveRecord::Base
scope :for_roles, ->(roles) do
where(role: roles)
end
end
Then you can put them together like this
Patient.for_physician(50).for_roles(["PI", "G2"]).uniq

In rails, how to query fields in join table?

In rails, I have two models, :contest and :problem. Each contest has many problems, and a problem can belong to many contests. A problem in different contests, may has differenet "index".
I use model :contest_problem to join :contest and :problem. In :contest_problem, I have a field index, to mark what's the "index" of problem in contest.
Now, when I execute Contest.find(...).problems.find(...) (... is contest_id or problem_id that I want to query), rails will create a sql query:
SELECT `problems`.*
FROM `problems`
INNER JOIN `contest_problems` ON `problems`.`id` = `contest_problems`.`problem_id`
WHERE `contest_problems`.`contest_id` = 1 AND `problems`.`id` = 1
LIMIT 1
I want to execute this sql query:
SELECT `problems`.*, `contest_problems`.`index`
FROM `problems`
INNER JOIN `contest_problems` ON `problems`.`id` = `contest_problems`.`problem_id`
WHERE `contest_problems`.`contest_id` = 1 AND `problems`.`id` = 1
LIMIT 1
How I should do in rails?
Models:
class Contest < ActiveRecord::Base
has_many :contest_problems
has_many :problems, through: :contest_problems
end
class Problem < ActiveRecord::Base
has_many :contest_problems
has_many :contests, through: :contest_problems
end
class ContestProblem < ActiveRecord::Base
belongs_to :contest
belongs_to :problem
end
Migration:
class CreateContestProblems < ActiveRecord::Migration
def change
create_table :contest_problems do |t|
t.integer :contest_id
t.integer :problem_id
t.string :index, limit: 2
t.timestamps
end
end
end
Try this query:
Problem.includes(:contests, :contest_ problems)
.where(#problem.id)
.where(contests: { id: #contest.id } ).first
This will give you, in one database query, the information that you need.

Rails 3 - select data from 3 tables

I have these 3 models:
class Car < ActiveRecord::Base
belongs_to :user
has_many :car_styles
has_many :styles, :through => :car_styles
end
class CarStyle < ActiveRecord::Base
belongs_to :car
belongs_to :style
end
class UserStyle < ActiveRecord::Base
belongs_to :style
belongs_to :user
end
class User < ActiveRecord::Base
has_many :cars
has_many :car_styles
has_many :styles, :through => :car_styles
end
I have logged-in a user (current_user). I am trying to select all cars, where car style_id are the same like user style_id -- how can I do that?
Thanks for help in advance
EDIT: schema:
cars
-id
-user_id
car_styles
-car_id
-style_id
user_styles
-user_id
-style_id
Every user has saved favorite styles - doesn't matter how much (but approximately ±5). Every photo has added a styles as well - and again doesn't matter how much.
And I would like to select all cars from table Cars, which have the same styles, as the current_user has added.
I think what you're saying here is you have three main models:
Car, User and Style
CarStyle and UserStyle are joins right?
If so, you should be able to say:
Car.joins(:styles => [:users]).where(:users => {:id => current_user.id })
If not, could you update the question with your schema?