Updating the values on Many-to-Many relationship table - mysql

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

Related

Multiple joins rails confusion

I have the following
**User** model
has_many orders class_name: "Order", foreign_key: "buyer_id"
**Order** model
has_many :items, class_name: "OrderItem"
**OrderItem** model
belongs_to :orderable, -> { with_deleted }, polymorphic: true
So a User has many Orders, each Order contains many OrderItems, and they have an "orderable_id" related to a product_id
I'm trying to return a list of User ids of users who have NOT purchased a certain item
Currently, i have
User.joins(:orders => :items).where("orders.status = 'success'").where("order_items.orderable_id != ?", product_id).pluck(:id)
Unfortunately, it doesn't go deep enough.
It is successfully discarding the OrderItem if it contains the offending product_id, but i need to to discard the User entirely if ANY of the OrderItems contain the offending product_id
it's just a bit over my head
You can try something like this:
user_who_purchased_ids = User.joins(orders: :items).where("orders.status = 'success'").where("order_items.orderable_id = ?", product_id).pluck(&:id)
users_who_did_not_purchase = User.where('id not in ?', user_who_purchased_ids)

how to add multiple foreign keys into the same row in Many to many in Ecto

Is it possible to insert multiple many to many relations at one? for example I have an AccessList table which has company_id, user_id, role_id, asset_id, and project_id
They are all many to many. AccessList is the reference table.
Here is the schema for the AccessList
schema "access_lists" do
belongs_to :user, Db.User
belongs_to :role, Db.Role
belongs_to :asset, Db.Asset
belongs_to :project, Db.Project
belongs_to :company, Db.Company
timestamps()
end
Here is the company schema as an example:
many_to_many :users, Db.User, join_through: Db.AccessList
many_to_many :assets, Db.Asset, join_through: Db.AccessList
many_to_many :roles, Db.Role, join_through: Db.AccessList
many_to_many :projects, Db.Project, join_through: Db.AccessList
At the moment for example if a request comes in I will, get the user, role, and company, then I create the asset and I insert their IDs at once into AccessList.
Sample:
def create_asset_relation(role, asset, user, company) do
changeset = create_asset_changeset( %{user_id: user.id, role_id: role.id, company_id: company.id, asset_id: asset.id})
with {:ok, _ } <- Repo.insert(changeset) do
{:ok, asset}
else
_ ->
"something gone wrong"
end
end
defp create_asset_changeset(params) do
AccessList.changeset(%AccessList{}, params)
end
Is it possible to insert multiple many to many relations at one?
Yes, your code example resembles the join schema example from the documentation:
defmodule UserOrganization do
use Ecto.Schema
#primary_key false
schema "users_organizations" do
belongs_to :user, User
belongs_to :organization, Organization
timestamps # Added bonus, a join schema will also allow you to set timestamps
end
def changeset(struct, params \\ %{}) do
struct
|> Ecto.Changeset.cast(params, [:user_id, :organization_id])
|> Ecto.Changeset.validate_required([:user_id, :organization_id])
# Maybe do some counter caching here!
end
end
defmodule User do
use Ecto.Schema
schema "users" do
many_to_many :organizations, Organization, join_through: UserOrganization
end
end
defmodule Organization do
use Ecto.Schema
schema "organizations" do
many_to_many :users, User, join_through: UserOrganization
end
end
# Then to create the association, pass in the ID's of an existing
# User and Organization to UserOrganization.changeset
changeset = UserOrganization.changeset(%UserOrganization{}, %{user_id: id, organization_id: id})
case Repo.insert(changeset) do
{:ok, assoc} -> # Assoc was created!
{:error, changeset} -> # Handle the error
end

Delete only join record between tables in Rails 4

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

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

Retrieve users having all of the groups from an array of groups with Rails

I have an app in which users have many groups and groups have many users, through a GroupUsers join table.
I'd like to get all the Users in exactly all of the groups of a given array of groups.
It sounds quite simple, yet I've not managed to find a way to do it.
This is what I tried (all_groups is the array of groups) :
users = User
all_groups.each do |group|
users = users.joins(:group_users).where("group_users.group_id = ?", group.id)
end
I always get no result, so I tried this to understand what's going on :
users = User
group1 = all_groups.first
group2 = all_groups.last
users1 = users.joins(:group_users).where("group_users.group_id = ?", group1.id)
users2 = users1.joins(:group_users).where("group_users.group_id = ?", group2.id)
users1 is an array with all the users in group1 as expected, but users2 is always empty.
Does someone have a clue about how to solve this ?
EDIT : Answer :
users = User
all_groups.each do |group|
users = User.including_ids(users.map(&:id)).joins(:group_users).where("group_users.group_id = ?", group.id)
end
With in models/user.rb :
scope :including_ids, ->(*ids) {
where(arel_table[:id].in(ids))
}
That's not really beautiful but it's working.
based on the explanation you gave, I assume you have something like:
class User
has_many :group_users
has_many :groups, through: :group_users
end
class GroupUser
belongs_to :user
belongs_to :group
end
class Group
has_many :group_users
has_many :users, through: :group_users
end
If yes, you can probably do the following:
# get groups
groups = Group.where( ......some condition here )
# as long as groups is an ActiveRecord::Relation type you can use 'pluck'
User.joins( :group_users ).where( 'group_users.group_id IN (?)', groups.pluck(:id) ).uniq
Hope that helps. :)
Cheers!