I'm doing a role permission edit function. It didn't have error but the logic of the code have some problem.
It didn't update the permission but update whole role_permission so it came out like image 1 & 2, it keep update many times.
I need to get the worker role_permission then check if that the permission already have?, if have then no need to add, if no then need to add(update). How can i check it at role controller update there?
Role controller
def edit
#role = Role.find(params[:id])
#role.company_id = params[:company_id]
#permissions = Permission.all
end
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
permission_ids = params[:permission_ids]
permission_ids.each do |permission_id|
RolePermission.update(role_id: #role.id, permission_id: permission_id)
end
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
Edit.html.erb
<% provide(:title, "Edit Roles") %>
<h1 class="dashboard">Update Role</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #role, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= #permissions.each do |permission|%>
<%= check_box_tag 'permission_ids[]', permission.id%>
<%= f.label :permission_name, permission.name %>
<% end %>
<%= f.hidden_field :company_id , value: 2%>
<%= f.submit "Save changes", class: "btn btn-secondary bottom" %>
<% end %>
</div>
</div>
RolePermission migration table
create_table "role_permissions", force: :cascade do |t|
t.integer "role_id"
t.integer "permission_id"
t.integer "company_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
parameter pass in console
parameter pass in console
Update
roles controller (role_params)
def role_params
params.require(:role).permit(:name, :company_id)
end
console logs when update role
console log
console log
Hi since you want Update/Add permissions related to roles.
can do it with the help of first_or_initialize:
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
permission_ids = params[:permission_ids]
permission_ids.each do |permission_id|
role_permissions = #role.role_permissions.where(permission_id: permission_id).first_or_initialize
role_permissions.save
end
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
Read more about first_or_initialize
OR
you should have the following associations with the below models:
# role.rb
has_many :role_permissions, dependent: :destroy
has_many :permissions, through: :role_permissions, source: :permission
# role_permission.rb
belongs_to :role
belongs_to :permission
Then in your roles_controller.rb should have below:
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
def role_params
params.require(:role).permit(:name, :company_id, permission_ids: [])
end
Related
I want to create a submission model where a user can create submissions for tasks. Each submission should have a user_id and a task_id. When I try to create a submission, rails returns an error saying that the task must exist.
task model:
has_many :submissions
user model:
has_many :submissions
submission model:
belongs_to :user
belongs_to :task
routes:
resources :tasks do
resources :submissions
end
submissions controller:
def create
#task = Task.find(params[:task_id])
#submission = current_user.submissions.build(submission_params)
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
def new
#task = Task.find(params[:task_id])
#submission = Submission.new
end
def show
#submission = Submission.find(params[:id])
end
private
def submission_params
params.require(:submission).permit(:description)
end
tasks/show.html.erb:
<% if user_signed_in? %>
<%= link_to "Submit", new_task_submission_path(#task) %>
<% end %>
submissions/new.html.erb:
<h2>Submit</h2>
<%= form_for [:task, #submission] do |f| %>
<div><%= hidden_field_tag :task_id, #task.id %></div>
<div class="field">
<%= f.text_area :description, placeholder: "File description" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
submission migration:
def change
create_table :submissions do |t|
t.string :description
t.integer :user_id
t.integer :task_id
t.timestamps
end
end
You are not assigning the task, you are only finding it in your create method. Please do this instead:
def create
#submission = current_user.submissions.build(submission_params)
#submission.task = Task.find(params[:task_id])
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
But Rails is able to perform this automatically, if you change the whitelisted params:
def create
#submission = current_user.submissions.build(submission_params)
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
private
def submission_params
params.require(:submission).permit(:description, :task_id)
end
This is an extension of this original question: Rails - Editing User and Profile Models from separate Settings Controller
My form works perfectly for editing a single model (Profile), however I have attempted to extend this to also allow for a user to edit some of the fields from the User model. Currently, the entire form is no longer saving any data - but I am not seeing any visible error messages in browser, other than the "success" message in my update method is not firing.
How can I successfully extend this setup to allow for both User and Profile fields to be saved in the same form? The form currently edits a Profile, and then allows fields_for a user - is this the wrong way around?
I have 2 models, User:
class User < ApplicationRecord
has_one :profile, dependent: :destroy
before_create :create_profile
private
def create_profile
build_profile(name: username)
end
end
and Profile:
class Profile < ApplicationRecord
belongs_to :user
accepts_nested_attributes_for :user
end
Both models are editable via the SettingsController:
class SettingsController < ApplicationController
def profile
#profile = User.find_by_id(current_user).profile
end
def update
set_profile
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_back fallback_location: settings_path, notice: 'Profile was successfully updated.' }
else
format.html { render :edit }
end
end
end
private
def profile_params
params.require(:profile).permit(:name, user_attributes: [:email])
end
end
On settings/profile, a user's profile is editable with the following form:
<h1>Settings</h1>
<div>
<div>
Name: <%= #profile.name %>
</div>
<%= form_with(model: #profile, url: update_settings_profile_path, local: true) do |form| %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<%= form.fields_for :user do |user_form| %>
<div class="field">
<%= user_form.label :email %>
<%= user_form.text_field :email %>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
</div>
Are here are the list of routes that show the profile page, and the update method for all of the other methods:
get 'settings', to: redirect('settings/profile')
get 'settings/profile', to: 'settings#profile', as: :settings_profile
patch 'settings', to: 'settings#update', as: :update_settings
Parameters when the form is submitted: (Removed auth token for clarity.)
Parameters: {"utf8"=>"✓", "authenticity_token"=>"X", "profile"=>{"name"=>"John Doe", "user_attributes"=>{"email"=>"test#email.com", "id"=>"22"}}, "commit"=>"Update Profile"}
Schema: (Basic columns removed for clarity.)
create_table "profiles", force: :cascade do |t|
t.string "name"
t.bigint "user_id"
...
t.index ["user_id"], name: "index_profiles_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "username", default: "", null: false
...
end
Appreciate any tips!
I have a very basic Photo and Comments model that works and then I have a built a Cflags model that is used to flag comments. I am getting the following error from Heroku log when I visit the photos/show.html.erb view.
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it)
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflags.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
PhotosController
def show
#photo = Photo.approved.find(params[:id])
end
CommentsController
def create
#photo = Photo.find(params[:photo_id])
#comment = #photo.comments.build(comment_params)
#comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
class CFlag < ActiveRecord::Base
belongs_to :comment, counter_cache: true
belongs_to :user, counter_cache: true
validates :user_id, presence: true
validates :comment_id, presence: true
validates :user_id, uniqueness: {
scope: [:comment_id],
message: 'You can only flag a comment once. Thank you for your feedback.'
}
default_scope -> { order(created_at: :desc) }
end
class CflagsController < ApplicationController
before_action :logged_in_user
def create
#comment = Comment.find(params[:comment_id])
#cflag = #comment.cflags.build(cflag_params)
if #cflag.save
if #comment.cflags_count > 1
#comment.update_attribute(:approved, false)
flash[:success] = "Flag created! Comment ##{#comment.id} has been removed for review. Thank you for your feedback"
redirect_to :back
else
flash[:success] = "Flag created! Thank you for your feedback"
redirect_to :back
end
else
redirect_to :back, notice: #cflag.errors.full_messages
end
end
private
def cflag_params
params.require(:cflag).permit(:user_id, :comment_id).merge(user_id: current_user.id)
end
end
resources :photos do
resources :comments do
resources :cflags
end
end
create_table "cflags", force: :cascade do |t|
t.integer "comment_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "cflags", ["comment_id"], name: "index_cflags_on_comment_id"
add_index "cflags", ["user_id"], name: "index_cflags_on_user_id"
If I change the form to:
<% #photo.comments.each do |comment| %>
<%= form_for([comment, comment.cflags.build]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
I get the same error:
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it):
There is a typo in the controller name.
Your model is CFlag with F uppercase but your controller has f lowercase.
Change it to CFlagsController
class CFlagsController < ApplicationController
before_action :logged_in_user
def create
...
end
end
I have a Rails 4.2 app which has 'Rooms', 'Bookings' and 'Extras'.
When making a booking it is for a room e.g. website.com/rooms/1/bookings/1
I have extras which I want to be associated with the booking for that room via check-boxes.
How can this be implemented? I've been reading about has_many :foo, :through => :bar associations but I'm not sure if that's the way to go.
The relevant code looks like this:
<!-- app\views\bookings\_form.html.erb -->
<%= form_for([#room, #booking]) do |f| %>
<p>
<%= f.label 'Select Customer:' %>
<%= f.collection_select :user_id, User.all, :id, :customer_name %>
</p>
<p>
<%= f.label 'start_time', 'Start Date and Time:' %>
<%= f.datetime_select :start_time, { minute_step: 15 } %>
</p>
<p>
<%= f.label 'length', 'Length of booking in hours:' %>
<%= f.number_field 'length', min: 1 %>
</p>
<p>
<%= f.label 'Room Price:' %>
<%= number_to_currency #room.price, unit: "£" %>
</p>
<p>
<%= f.label 'Extras:' %>
<%= f.collection_check_boxes :extra_ids, Extra.all, :id, :extra_info %>
</p>
<%= f.submit 'Submit' %>
<% end %>
# app\models\booking.rb
class Booking < ActiveRecord::Base
belongs_to :room
belongs_to :user
has_many :additions
has_many :extras, :through => :additions
end
# app\models\extra.rb
class Extra < ActiveRecord::Base
belongs_to :extracat
has_many :additions
has_many :bookings, :through => :additions
def extra_info
"#{name}"
end
end
# This model is for the has_many through testing I tried
# app\models\addition.rb
class Addition < ActiveRecord::Base
belongs_to :booking
belongs_to :extra
end
# Relevant section of schema
create_table "additions", force: :cascade do |t|
t.integer "booking_id"
t.integer "extra_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "bookings", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.integer "length"
t.integer "room_id"
t.integer "user_id"
t.integer "extra_id"
end
EDIT - The section within the bookings show page.
# app\views\bookings\show.html.erb
<% #booking.extras.each do |e| %>
<%= e.name %>,
<% end %>
EDIT - Adding bookings controller
class BookingsController < ApplicationController
respond_to :html, :xml, :json
before_action :find_room
def index
#bookings = Booking.where("room_id = ? AND end_time >= ?", #room.id, Time.now).order(:start_time)
respond_with #bookings
end
def new
#booking = Booking.new(room_id: #room.id)
end
def create
#booking = Booking.new(params[:booking].permit(:room_id, :start_time, :length))
#booking.room = #room
if #booking.save
redirect_to room_bookings_path(#room, method: :get)
else
render 'new'
end
end
def show
#booking = Booking.find(params[:id])
end
def destroy
#booking = Booking.find(params[:id]).destroy
if #booking.destroy
flash[:notice] = "Booking: #{#booking.start_time.strftime('%e %b %Y %H:%M%p')} to #{#booking.end_time.strftime('%e %b %Y %H:%M%p')} deleted"
redirect_to room_bookings_path(#room)
else
render 'index'
end
end
def edit
#booking = Booking.find(params[:id])
end
def update
#booking = Booking.find(params[:id])
# #booking.room = #room
if #booking.update(params[:booking].permit(:room_id, :start_time, :length))
flash[:notice] = 'Your booking was updated succesfully'
if request.xhr?
render json: {status: :success}.to_json
else
redirect_to resource_bookings_path(#room)
end
else
render 'edit'
end
end
private
def save booking
if #booking.save
flash[:notice] = 'booking added'
redirect_to room_booking_path(#room, #booking)
else
render 'new'
end
end
def find_room
if params[:room_id]
#room = Room.find_by_id(params[:room_id])
end
end
def booking_params
params.require(:booking).permit(:user_id, :extra_id)
end
end
How is it possible to associate the extras with a booking? As so far they are not being saved with the booking into the database. Is this a controller issue?
You're not permitting the parameters correctly - the name is extra_ids. In addition since the parameter is an array you need to permit it like so:
params.require(:booking).permit(:room_id, :start_time, :length, :extra_ids => [])
Personally I recommend setting action controller to raise an error when unpermitted parameters are encountered in development or tests - very easy otherwise to miss the log messages
I have user, team and team_user model. team_use is joining model as a result of has_many through association between user and team. team_user has user_id and team_id from user and team tables. team has name and team_lead_id( which would a user from user table).
Problem:
When I create new team, I want to add users and team lead to team. A team can have multiple user but only one team lead. While creating team , user_id and team_id should be saved in team_user table. But this is not happening. I tried with following code in team_controller.rb:
def create
#team = Team.new(params[:team])
#user_team = TeamUser.new( { user_id: '#user.id', team_id: 'params[:team_id]' } )
if #team.save
flash[:notice] = 'Team has been created'
redirect_to teams_path
else
flash[:alert] = 'Team not created'
redirect_to teams_path
end
end
With TeamUser.new( { user_id: '#user.id', team_id: 'params[:team_id]' } ) , I tried to create user and team entries in team_user table but failed.
Code is following.
teams_controlller.rb
class TeamsController < ApplicationController
before_filter :authorize_admin!
def index
#teams = Team.all
#team = Team.new
end
def new
#team = Team.new
end
def create
#team = Team.new(params[:team])
#user_team = TeamUser.new( { user_id: '#user.id', team_id: 'params[:team_id]' } )
if #team.save
flash[:notice] = 'Team has been created'
redirect_to teams_path
else
flash[:alert] = 'Team not created'
redirect_to teams_path
end
end
def show
#team = Team.find(params[:id])
end
def edit
#team = Team.find(params[:id])
end
def update
#team = Team.find(params[:id])
if #team.update_attributes(params[:team])
flash.notice = "Team #{#team.name} has been updated"
redirect_to team_path
else
render 'edit'
end
end
def destroy
#team = Team.find(params[:id])
#team.destroy
redirect_to action: 'index'
end
end
team.rb
class User < ActiveRecord::Base
has_many :roles, through: :role_users
has_many :role_users
has_many :leaves
serialize :role
has_many :teams, through: :team_users
before_save :make_array
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :username, :email, :password, :password_confirmation,
:remember_me, :first_name, :last_name, :is_admin, :contact_no, :birth_date,
:joining_date, :is_active, :role, :is_manager, :user_code, :designation
# attr_accessible :title, :body
def active_for_authentication?
super && is_active?
end
def make_array
self.role.reject!(&:blank?) if self.role
end
end
team_user.rb
class TeamUser < ActiveRecord::Base
belongs_to :user
belongs_to :team
attr_accessible :team_id, :team_lead_id, :user_id
end
team.rb
class Team < ActiveRecord::Base
has_many :users, through: :team_users
has_many :team_users
attr_accessible :name, :team_lead_id
end
_form.html.erb(team)
<%= form_for #team do |f| %>
<% #team.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<div style=" margin-top:10px">
<label> Team Name </label>
<%= f.text_field :name, :class => 'text_field' %>
</div>
<label> Add Users </label>
<%= select_tag "TeamUser[user_id]", options_from_collection_for_select( User.all, "id", "first_name"), :style => "width:270px; height:35px", :id => "drp_Books_Ill_Illustrations",
:class => "leader MultiSelctdropdown Books_Illustrations" %>
<label> Team Lead </label>
<%= f.select(:team_lead_id, User.all.map { |u| [u.first_name, u.id] }) %>
<div class=modal-footer>
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<%= f.submit "Create Team", :class => 'btn btn-primary' %>
</div>
<% end %>
schema.rb
create_table "team_users", :force => true do |t|
t.integer "team_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "teams", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "team_lead_id"
end
I am able to create team with team_lead_id in team model but failed to save data in team_user team.
The reason it's not being saved is that you call new instead of create. Besides, when you are trying to create TeamUser record, you don't know #team.id yet it's not saved at this time. But there's also other things that need to be fixed. First, your new action should look:
def new
#team = Team.new
#users = User.all
end
Now you don't need to call User.all twice in your view, which isn't very good practice.
Now, the view:
<%= form_for #team do |f| %>
<% #team.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<div style=" margin-top:10px">
<label> Team Name </label>
<%= f.text_field :name, :class => 'text_field' %>
</div>
<label> Add Users </label>
<%= select_tag "users[]", options_from_collection_for_select( #users, :id, :first_name), :style => "width:270px; height:35px", :id => "drp_Books_Ill_Illustrations",
:class => "leader MultiSelctdropdown Books_Illustrations", :multiple => true %>
<label> Team Lead </label>
<%= select_tag(:team_lead_id, options_from_collection_for_select(#users, :id, :first_name)) %>
<div class=modal-footer>
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<%= f.submit "Create Team", :class => 'btn btn-primary' %>
</div>
<% end %>
In your create action:
def create
#team = Team.new(params[:team])
team_lead = User.find(params[:team_lead_id])
#team.team_lead = team_lead
if #team.save
users = User.where(:id => params[:users])
users.each {|user| #team.users << user}
flash[:notice] = 'Team has been created'
redirect_to teams_path
else
flash[:alert] = 'Team not created'
redirect_to teams_path
end
end
And you should also update your Team model:
class Team < ActiveRecord::Base
has_many :users, through: :team_users
has_many :team_users
belongs_to :team_lead, class_name: 'User'
attr_accessible :name
end
Since you display form not only in new action, I advice you to make before_filter in your controller which would set variables needed for form:
before_filter :set_form_variables, only: [:new, :index] # everywhere the form is displayed
# ...
private
def set_form_variables
#team = Team.new
#users = User.all
end