How to update data in join table in Rails 4 - ruby-on-rails

I am building an app with Rails 4 using Devise and CanCan. When a new user registers they are not given a default role. The admin has to manually assign it through the admin panel. And that's where I am running into difficulty. I can't figure out how to update the data in the join field. Here's what I have so far.
My UsersController:
class UsersController < ApplicationController
before_filter :authenticate_user!
before_action :set_user, only: [:show, :update, :destroy]
def index
authorize! :index, #user, message: 'Not authorized as an administrator.'
#users = User.all
end
def show
end
def update
authorize! :update, #user, message: 'Not authorized as an administrator.'
if #user.update_attributes(user_params)
redirect_to users_path, notice: "User updated."
else
redirect_to users_path, alert: "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, message: 'Not authorized as an administrator.'
unless #user == current_user
#user.destroy
redirect_to users_path, notice: "User deleted."
else
redirect_to users_path, notice: "Can't delete yourself."
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email, :role_id, :user_id)
end
end
And my role model:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
And my user model:
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
As you can see it's pretty much the standard model as generated by Devise.
And here's the form I'm using to update the role:
<div id="role-options-<%= user.id %>" class="reveal-modal medium" style="display: none;">
<%= simple_form_for user, url: user_path(user), html: {method: :put, class: 'custom' } do |f| %>
<h3>Change Role</h3>
<%= f.input :role_ids, collection: Role.all, as: :radio_buttons, label_method: lambda {|t| t.name.titleize}, label: false, item_wrapper_class: 'inline', checked: user.role_ids.first %>
<%= f.submit "Change Role", class: "small button" %>
<a class="close-reveal-modal" href="#">Close</a>
<% end %>
</div>
When I check the new role on the form and submit it, I am redirected to the users page with the message "User updated." However, the user role has not been updated.
I'm still fairly new to Rails and just can't figure out exactly what I am doing wrong. If I understand correctly, I need to update the data in the user_roles table. I just can't figure out what I'm doing wrong.

You are using string params, but forgot to add role_ids into allowed fields.
So, your controller should contain something like this (in case role_ids is an array):
def user_params
params.require(:user).permit(:name, :email, :role_ids => [])
end
Or if role_ids is scalar, just drop => [] part.
More info could be found here

I am not sure what rolify does. But I would do this:
class User
has_one :users_roles
has_one :role, through: :users_roles
I am not sure about this though. But you could give it a try. I know has_one :users_roles is weird.

Related

How to create the backend to multiple user types displayed in radio buttons? Devise and Rails

I'm fairly new to rails. I took an idea from another user when creating multiple user types using STI. So, at this point, I have two user types, teachers and students, and they all are shown as
Class Teacher < User
Class Student < User
I used Devise which automatically create a signup (registrations path) and a user model.
<div class="authform">
<%= form_for #users(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
And then skipping ahead to the radio buttons:
<div>
<%= "I am a..." %>
<%= radio_button_tag(:user, "student") %>
<%= label_tag(:student, "student") %>
<%= radio_button_tag(:user, "teacher") %>
<%= label_tag(:teacher, "teacher") %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %>
</div>
How do I attach this to backend and user types? Here is my user.rb
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
def skip_confirmation!
self.confirmed_at = Time.now.utc
end
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
#:confirmable,
:recoverable, :rememberable, :trackable, :validatable
def to_param
username
end
end
And here is my user controller:
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
redirect_to 'classrooms#index'
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def secure_params
params.require(:user).permit(:role)
end
end
I'm not sure what you were trying to do.
If you were trying to send data which is from radio button, you can add a form_tag.
Before that, we should probably define our controller.
For example, I define a create_user_type in user_controller.rb
def create_user_type
byebug ## it will bind your termianl and help you to see the parameter
end
Then edit routes.rb, add this line.
post 'users/create_user_type' => 'users/create_user_type'
Finally, change your form.html.erb
<%= form_tag users_create_user_type_url do |f| %>
<%= radio_button_tag(:user, "student") %>
<%= label_tag(:student, "student") %>
<%= radio_button_tag(:user, "teacher") %>
<%= label_tag(:teacher, "teacher") %>
<%= submit_tag "Save!"%>
<% end %>
So, in your browser, you can see this
After you click the save button, you should notice your terminal is binding.
Now you can print the params to see what's the data from the radio button.

Rails: NoMethodError on creating has_one association with devise model

I am a complete beginner in Rails as such and I am trying to build a page to add extra profile data once the user logs in.
I am using Devise for authentication purposes and that works fine. I get this error and I have been stuck here.
undefined method `profiles'
Can you please help?
Codes
profiles_controller.rb
class ProfilesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create, :show]
def new
#profile = current_user.profiles.build
end
def create
#profile = current_user.profiles.build(profile_params)
if #profile.save
format.html {redirect_to #profile, notice: 'Post was successfully created.'}
else
format.html {render 'new'}
end
end
def show
#profile = current_user.profiles
end
private
def profile_params
params.require(:profile).permit(:content)
end
end
The error seems to be coming from these lines in particular
def new
#profile = current_user.profiles.build
end
Other codes for reference:
/views/profiles/new.html.erb
<h1>Profiles#new</h1>
<p>Find me in app/views/profiles/new.html.erb</p>
<h3>Welcome <%= current_user.email %></h3>
<%= form_for(#profile) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :text, autofocus: true %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<%end%>
routes.rb
Rails.application.routes.draw do
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
get 'pages/home'
get 'pages/dashboard'
devise_for :users, controllers: { registrations: "registrations" }
resources :profiles
root 'pages#home'
devise_scope :user do
get "user_root", to: "page#dashboard"
end
end
models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile, dependent: :destroy
end
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
I just figured it out!
As the relationship is has_one, we should be using
def new
#profile = current_user.build_profile
end
instead of
def new
#profile = current_user.profiles.build
end
according to the documentation -
http://guides.rubyonrails.org/association_basics.html#has-one-association-reference
1) If your user must have many profiles. Set in your app/models/user.rb has_many :profiles
2) In your ProfilesController in new method instead of #profile = current_user.profiles use #profile = Profile.new
3) In your routes.rb delete
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
because you have already used resources :profiles
4) To stay with rules of DRY you can render form from a partial. Just add in views/profiles/_form.html.erb with the same content in your new.html.erb and after this you can delete everything im new.htm.erb and paste <%= render "form" %>. In future it will help you to render edit form if you want.
5) In your ProfilesController you can add method index with all profiles
def index
#profiles = Profile.all
end
You are trying to call an undefined relationship:
def new
#profile = current_user.profiles.build
end
has_one :profile
You should be calling:
def new
#profile = current_user.build_profile
end

Model does not seem to be getting saved?

So I am using the devise gem to generate users for my web app. I have a Business model that is a has_one: association with my User. When I attempt to create a business when logged into my user it simply does not save it. Also when i use rails console in the terminal attempting to open up my Business model returns nil. In the view that is calling .business on the user shows nothing
Here is my business model
class Business < ActiveRecord::Base
belongs_to :user
end
User Model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :name, presence: true
has_many :posts, dependent: :destroy
has_one :business, dependent: :destroy
has_attached_file :avatar,
styles: { medium: "300x300#", thumb: "100x100#", post_pic: "44x44" },
default_url: "/images/:style/missing.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
end
Business controller
class BusinessesController < ApplicationController
before_action :set_business, only: [:show, :edit, :update, :destroy]
def index
#businesses = Business.all
end
def show
end
def new
#business = current_user.build_business
end
def edit
end
def create
#business = current_user.build_business(business_params)
if #business.save
redirect_to #business, notice: 'Business was successfully created.'
else
render :new
end
end
def update
if #business.update(business_params)
redirect_to #business, notice: 'Business was successfully updated.'
else
render :edit
end
end
def destroy
#business.destroy
redirect_to businesses_url, notice: 'Business was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_business
#business = Business.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def business_params
params.require(:business).permit(:name, :desc, :avatar)
end
end
VIEW THAT CALLS THE BUSINESS
<div class="row">
<div class="col-md-7">
<div class="panel panel-default">
<div class="panel-body">
<%= image_tag current_user.business.avatar.url(:thumb), class:"img-thumbnail" %>
<%= current_user.business.name %>
<%= current_user.business.desc %>
<%= link_to 'create business', new_business_path, class:"pull-right" %>
</div>
</div>
</div>
</div>
Do you have a user_id column in your business table? It would look like this: t.integer "user_id"
Then, to directly associate the business with the user, in businesses_controller.rb I would write the new and create methods in that way:
def new
#business = current_user.businesses.new
end
def create
#business = current_user.businesses.new(business_params)
if #business.save
redirect_to #business, notice: 'Business was successfully created.'
else
render :new
end
end
Hope this helps.

Rails - nested model input data not appearing in "show" page

I am having trouble figuring out how to make some data collected through a nested model appear on the "show" page. I have a rails app with 3 models, a User model, a Project model, and a Team model. The model associations are as follows:
Project:-
class Project < ActiveRecord::Base
has_many :users, :through => :team
has_one :team, :dependent => :destroy
accepts_nested_attributes_for :team, allow_destroy: true
end
Team:-
class Team < ActiveRecord::Base
belongs_to :project
has_many :users
end
User:-
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :teams
end
Every project has one team, and every team consists of many users who are already saved in the database. What I would like to do exactly is to make it possible to select multiple existing users within the project form (through a nested form) and save them to a model called team. I managed to get the form working correctly, but im not sure how to go about saving the data collected to the team model, and then to make the group of users that were selected (the team) to appear in project's show page. Please help!
P.S I used the nested form gem to add multiple team members within the project's form.
Projects Show page:-
<%= bootstrap_nested_form_for(#project, :html => {:multipart => true}, layout: :horizontal) do |f| %>
<% f.fields_for :teams do |builder| %>
<% if builder.object.new_record? %>
<%= builder.collection_select :user, User.all, :id, :email, { prompt: "Please select", :selected => params[:user], label: "Employee" } %>
<% else %>
<%= builder.hidden_field :_destroy %>
<%= builder.link_to_remove "Remove" %>
<% end %>
<%= f.link_to_add "Add Team Member", :teams %>
<%= f.submit %>
<% end %>
projects controller:-
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#projects = Project.all
respond_with(#projects)
end
def show
respond_with(#project)
end
def new
#project = Project.new
#project.pictures.build
#project.teams.build
respond_with(#project)
end
def edit
#project = Project.find(params[:id])
#project.pictures.build
#project.teams.build
end
def create
#project = Project.new(project_params)
if #project.save
flash[:notice] = "Successfully created project."
redirect_to #project
else
render :action => 'new'
end
end
def update
#project.update(project_params)
respond_with(#project)
end
def destroy
#project.destroy
respond_with(#project)
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:id, :title, :description, :status, :phase, :location, :image, pictures_attributes: [:id, :image], teams_attributes: [:project_id, :user_id])
end
end

Rails - Nested form not showing on "show" page

I am having trouble figuring out how to make some data collected through a nested model appear on the "show" page. I have a rails app with 3 models, a User model, a Project model, and a Team model. The model associations are as follows:
Project:-
class Project < ActiveRecord::Base
has_many :users, :through => :team
has_one :team, :dependent => :destroy
accepts_nested_attributes_for :team, allow_destroy: true
end
Team:-
class Team < ActiveRecord::Base
belongs_to :project
has_many :users
end
User:-
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :teams
end
Every project has one team, and every team consists of many users who are already saved in the database. What I would like to do exactly is to make it possible to select multiple existing users within the Project form (through a nested form) and save them to a model called Team. I managed to get the form working correctly, but im not sure how to go about saving the data collected to the team model, and then to make the group of users that were selected (the team) to appear in project's show page, as there are 3 models involved. The Please help!
P.S I used the nested form gem to add multiple team members within the project's form.
Projects Show page:-
<%= bootstrap_nested_form_for(#project, :html => {:multipart => true}, layout: :horizontal) do |f| %>
<% f.fields_for :teams do |builder| %>
<% if builder.object.new_record? %>
<%= builder.collection_select :user, User.all, :id, :email, { prompt: "Please select", :selected => params[:user], label: "Employee" } %>
<% else %>
<%= builder.hidden_field :_destroy %>
<%= builder.link_to_remove "Remove" %>
<% end %>
<%= f.link_to_add "Add Team Member", :teams %>
<%= f.submit %>
<% end %>
projects controller:-
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#projects = Project.all
respond_with(#projects)
end
def show
respond_with(#project)
end
def new
#project = Project.new
#project.pictures.build
#project.teams.build
respond_with(#project)
end
def edit
#project = Project.find(params[:id])
#project.pictures.build
#project.teams.build
end
def create
#project = Project.new(project_params)
if #project.save
flash[:notice] = "Successfully created project."
redirect_to #project
else
render :action => 'new'
end
end
def update
#project.update(project_params)
respond_with(#project)
end
def destroy
#project.destroy
respond_with(#project)
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:id, :title, :description, :status, :phase, :location, :image, pictures_attributes: [:id, :image], teams_attributes: [:project_id, :user_id])
end
end

Resources