I have user created groups in my application. I'm confused as to how to set the user that creates the group as an owner. I want there to be able to be multiple owners so it's a 'has-many-through' relationship. I can create/edit/delete a group.
So my question is how do I insert the current user_id and the group_id into the group_owners table at the time that the group is created?
Here is what I have that works so far:
User Model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :group_owners
has_many :user_groups, through: :group_owners
end
Group Model
class UserGroup < ActiveRecord::Base
has_many :goup_owners
has_many :users, through: :groups_owners
validates :name, presence: true, length: {minimum: 5}
validates :visibility, presence: true, length: {minimum: 5}
VISIBILITY_TYPES = ["Public", "Private"]
end
Group Owner Model
class GroupOwner < ActiveRecord::Base
belongs_to :user
belongs_to :user_group
end
User Groups Controller - Create Action
def create
#usergroup = UserGroup.new(usergroup_params)
if #usergroup.save
redirect_to user_groups_path
else
render 'new'
end
end
I assume something needs to go in the user group create method but I'm not sure what.
Thanks for any help you can offer.
You should create UserGroup like
def create
#usergroup = current_user.user_groups.build(usergroup_params)
if #usergroup.save
redirect_to user_groups_path
else
render 'new'
end
end
This way the user group will be created with current users id and the group id into the group owners table.
In you UserGroup model, set a boolean for owner.
create_table |t|
t.references :user
t.references :group
t.boolean :owner
end
class UserGroup < ActiveRecord::Base
belongs_to :user
belongs_to :owner
scope :groups, ->(*g) {where(group_id: g.flatten.compact.uniq)}
scope :users, ->(*u) { where(user_id: u.flatten.compact.uniq)}
scope :owners, ->{where owner:true}
end
class User < ActiveRecord::Base
has_many :user_groups, dependent: :destroy, inverse_of: user
has_many :groups, through: :user_groups
def owned_groups
groups.merge(UserGroup.owners)
end
end
The following changes to the user group controller create method fixed my issue.
def create
#user_group = current_user.user_groups.build(usergroup_params)
if #user_group.save
#user_group.users << current_user
redirect_to user_groups_path
else
render 'new'
end
end
Related
My goal is to display in the notifications modal that a follower has started following the current user whenever the user clicks the follow button.
I am also using the noticed gem, but it seems a bit complicated for me to implement with my relationship model (Which is the follower/following model).
Whenever I follow someone, I see in the console that it is inserting the notification, but when I click unfollow I get an error that there are "too many has_many associations". And when I log in as the user that gets followed the notification does not appear. I am assuming because I have implemented the notify recipient function wrong.And I cannot seem to find any resources only for follow notifications.
Here is my code:
FollowNotification.rb
def message
#user = User.find(follower_id: params[:user_id])
"#{#user.username} has started following you"
end
#
def url
show_user_path(#user)
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!
def create
#user = User.find(params[:user_id])
# if statement prevents user from forcing both users to follow each other after accepting request
if current_user.Is_private? && !#user.pending_requests
following = #user.relationships.build(follower_id: current_user.id)
following.save
redirect_to request.referrer || root_path
else
following = current_user.relationships.build(follower_id: params[:user_id])
following.save
redirect_to request.referrer || root_path
end
end
def destroy
following = current_user.relationships.find_by(follower_id: params[:user_id])
following.destroy
redirect_to request.referrer || root_path
end
end
Relationship model
class Relationship < ApplicationRecord
belongs_to :following, class_name: 'User'
belongs_to :follower, class_name: 'User'
has_noticed_notifications model_name: 'Notification'
has_many :notifications, through: :user, dependent: :destroy
after_create_commit :notify_recipient
before_destroy :cleanup_notifications
private
def notify_recipient
FollowNotification.with(follower: self).deliver_later(following.id)
end
def cleanup_notifications
notifications_as_follow.destroy_all
end
end
User model
class User < ApplicationRecord
has_merit
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar
validates :avatar, file_size: { less_than_or_equal_to: 5.megabytes },
file_content_type: { allow: ['image/jpg', 'image/png', 'image/jpeg'] }
has_many(
:posts,
class_name: 'Post',
foreign_key: 'user_id',
inverse_of: :user
)
has_many :likes
has_many :comments
validates :username, presence: true, length: {maximum: 30}
validates_uniqueness_of :username
has_many :relationships, foreign_key: :following_id
has_many :followings, through: :relationships, source: :follower
has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id
has_many :followers, through: :reverse_of_relationships, source: :following
def is_followed?(user)
reverse_of_relationships.find_by(following_id: user.id).present?
end
has_many :notifications, as: :recipient, dependent: :destroy
end
In the context where a user.admin has_many hotels, is there a way to invite a user to 1 hotel only? (e.g. many-to-many relationship between user and hotel, with join UserHotel table).
More concretely, the first problem I am encountering is that I am not able to properly insert the hotel_id param in the users/invitations_controller.
Error message:Couldn't find Hotel without an ID. params sent: {"format"=>"109"}
Please find my current code below=>
views/hotels/show
<%= link_to "invite new user", new_user_invitation_path(#hotel) %>
routes
Rails.application.routes.draw do
devise_for :users, controllers: {
invitations: 'users/invitations'
}
resources :hotels do
resources :users
end
end
models
class User < ApplicationRecord
has_many :user_hotels, dependent: :destroy
has_many :hotels, through: :user_hotels
enum role: [:owner, :admin, :employee]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :admin
end
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :invitable
end
class UserHotel < ApplicationRecord
belongs_to :hotel
belongs_to :user
end
class Hotel < ApplicationRecord
has_many :user_hotels, dependent: :destroy
has_many :users, through: :user_hotels
accepts_nested_attributes_for :users, allow_destroy: true, reject_if: ->(attrs) { attrs['email'].blank? || attrs['role'].blank?}
end
controllers/users/invitations
class Users::InvitationsController < Devise::InvitationsController
def new
#hotel = Hotel.find(params[:hotel_id])
#user = User.new
How to build the join table UserHotel when inviting?
end
end
add this code to app/models/user.rb:
accepts_nested_attributes_for :user_hotels
and then:
User.new(user_hotels_attributes: [{ hotel: #hotel }])
you can add your own validations to prevent duplicate entry.
I'm currently implementing pundit, where I am trying to identify whether or not a user has an admin role.
Issue
I'm trying to avoid creating a join_table between discounts and users, by leveraging the relationship between
discounts and attraction (a discount belongs to an attraction)
attractions and park (a park has_many attractions)
parks and users (many to many relationship, via a join_table).
--> However, I get the error message: undefined local variable or method `attraction' for #<DiscountPolicy::Scope:0x00007fa012ec6b70>
Question
I was wondering:
if it's even possible what I'm trying to do and if so
how will I be able to access the user?
Code
discount controller
def index
#user = current_user
if params[:attraction_id]
#attraction = Attraction.find(params[:attraction_id])
#discounts = #attraction.discounts
#discounts = policy_scope(#discounts)
else
#discounts = []
end
end
discount policy
class DiscountPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
# scope.where(user: user)
scope.joins(attraction: :discounts).where(discounts: { attraction_id: attraction.id }).joins(park: :attractions).where(attractions: { park_id: park.id }).joins(park: :user_parks).where(user_parks: { user_id: user.id })
else
raise Pundit::NotAuthorizedError
end
end
end
def index?
user.admin?
end
end
models
class Discount < ApplicationRecord
belongs_to :attraction
has_many :reservations
end
class Attraction < ApplicationRecord
belongs_to :park
has_many :discounts, dependent: :destroy
accepts_nested_attributes_for :discounts, allow_destroy: true
end
class Park < ApplicationRecord
has_many :attractions, dependent: :destroy
has_many :discounts, through: :attractions
has_many :user_parks, dependent: :destroy
has_many :users, through: :user_parks
accepts_nested_attributes_for :users, allow_destroy: true, reject_if: ->(attrs) { attrs['email'].blank? || attrs['role'].blank?}
end
class UserPark < ApplicationRecord
belongs_to :park
belongs_to :user
end
class User < ApplicationRecord
has_many :user_parks, dependent: :destroy
has_many :parks, through: :user_parks
enum role: [:owner, :admin, :employee, :accountant]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :admin
end
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :invitable
end
You need to have nested association joins. Here's what your scope should look like:
scope.joins(attraction: [park: :user_parks]).where(user_parks: { user_id: user.id })
You can go through the documentation to understand better.
I need some help modeling my models and controller. Here is what I want to achieve:
I want to have a devise user named User (as usual) and a second model named Project. A Project should belong to a single User and at the same time should have many participants. The participants in a project should also be users (with devise registration/login) but the user, that created the project should not be able to participate.
So far, so good. Here comes the tricky part: In my controller I want to be able to write:
def participate
p = Project.find(id: params[:id])
p.participants << current_user unless p.participants.includes?(current_user) && !p.user_id.equal(current_user.id)
if p.save
redirect_back
else
render :project
end
end
This doesn't work because p.participants is not an array and the query (I tried it in rails console) does not check my n:m table.
Here is my current model setup:
class Project < ApplicationRecord
before_validation :set_uuid, on: :create
validates :id, presence: true
belongs_to :user
has_and_belongs_to_many :participants, class_name: "User"
end
class User < ApplicationRecord
before_validation :set_uuid, on: :create
validates :id, presence: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_and_belongs_to_many :projects
end
Finally my migrations:
class CreateProjects < ActiveRecord::Migration[6.0]
def change
create_table :projects, id: false do |t|
t.string :id, limit: 36, primary_key: true
t.string :title
t.belongs_to :user, index: true, foreign_key: true, type: :uuid
t.datetime :published_at
t.timestamps
end
end
end
class CreateJoinTableProjectsUsers < ActiveRecord::Migration[6.0]
def change
create_join_table :users, :projects do |t|
t.index :project_id
t.index :user_id
end
end
end
It is better to use has_many: through instead of has_and_belongs_to_many. This allows you to write cleaner code for validation.
Remove has_and_belongs_to_many from User and Project models
Add has_many :through to User and Project models
rails g model UserProject user:references project:references
rails db:migrate
class User < ApplicationRecord
..
has_many :user_projects
has_many :projects, through: :user_projects
..
end
class Project < ApplicationRecord
..
has_many :user_projects
has_many :participants, through: :user_projects, source: 'user'
..
end
class UserProject < ApplicationRecord
belongs_to :user
belongs_to :project
end
Add validation to UserProject model
class UserProject < ApplicationRecord
belongs_to :user
belongs_to :project
validate :check_participant
private
def check_participant
return if project.participants.pluck(:id).exclude?(user.id) && project.user != user
errors.add(:base, 'You cannot be participant')
end
end
Update participate method
def participate
p = Project.find(id: params[:id])
begin
p.participants << current_user
redirect_back
rescue ActiveRecord::RecordInvalid => invalid
puts invalid.record.errors
render :project
end
end
I am building a ROR app with Users and Groups and I want to be able to link them with an associative model called Memberships.
My problem is that when I try to create a group with one member, the members array for the new group is always empty.
I tried creating a group like this:
def create (user)
#group = Group.new(create_group_params)
user.join(#group)
user.save
#group.save
end
but #group.members is empty when I print it out. How can I make the user a member of the group?
Here are my models:
Group.rb
class Group < ActiveRecord::Base
has_many :passive_memberships, class_name: "Membership",
foreign_key: "club_id",
dependent: :destroy
has_many :members, through: :passive_memberships, source: :member
end
User.rb
class User < ActiveRecord::Base
has_many :active_memberships, class_name: "Membership",
foreign_key: "member_id",
dependent: :destroy
has_many :memberships, through: :active_memberships, source: :club
def join(group)
active_memberships.create(club_id: group.id)
end
end
Membership.rb
class Membership < ActiveRecord::Base
belongs_to :member, class_name: "User"
belongs_to :club, class_name: "Group"
validates :member_id, presence: true
validates :club_id, presence: true
end
Oh. I have to save the group before the user can join it.
def create (user)
#group = Group.new(create_group_params)
#group.save
user.join(#group)
user.save
end