ruby on rails 4 mass assignment - ruby-on-rails-4

I have:
a model User for common data such as email, password, first_name, last_name
a model Student for specific data such as faculty_id, etc..
What i want is to have a possibility to modify student's profile, e.g change its faculty and credentials. I found out that accepts_nested_attributes_for should help me with that. So that is what i have:
class User < ActiveRecord::Base
has_one :student
has_one :header
has_one :admin
has_many :progresses, :through => :student
accepts_nested_attributes_for :student
end
class Student < ActiveRecord::Base
belongs_to :user
belongs_to :group
has_one :specialization, through: :group
has_many :progresses
scope :activated, lambda {(where("users.activated = 'off'"))}
self.per_page = 1
end
class Admin::StudentsController < AdminController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#students = Student.joins(:user).joins(:group).paginate(:page => params[:page]).all
end
def show
end
def edit
end
def update
end
private
def set_user
#student = Student.find(params[:id])
#student.build_user
end
end
Form partial
= form_for([:admin,#student]) do |f|
- if #student.errors.any?
#error_explanation
h2
= pluralize(#student.errors.count, "error")
| prohibited this group from being saved:
ul
- #student.errors.full_messages.each do |msg|
li= msg
.field
= f.fields_for :user do |builder|
= builder.label :first_name
br/
= builder.text_field :first_name
.field
= f.label :faculty_id
br/
= f.text_field :faculty_id
.actions
= f.submit
I looked into inspect element in a browser and input for user object is
<input id="student_user_first_name" name="student[user][first_name]" type="text">
which seems correct. But the field is empty. Please tell me where i went wrong? Thank you.

Rails 4 uses Strong Parameters, so you need to whitelist your params in the controller. Here is a good explanation which considers the use of accepts_nested_attribute_for. http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html

Related

rails 4 semantic ui multi-select show user selections on edit

I am trying to implement multi-select in a form in rails 4, using Semantic UI. I want users to be able to select multiple categories for each post. So far, I am able to display the dropdown select field which pulls all the categories from the database as follows:
<%= form_for #post, :html => {:class => "ui form bk-form group"} do |f| %>
<label>Post title:</label><br />
<%= f.text_field :title, autofocus: true %>
<label>Choose a category:</label><br />
<%= f.select(:category_ids,options_for_select(Category.all.collect{|x| [x.name,x.id,class:'item']}),{prompt:'Select categories'}, multiple: true, class:'ui fluid dropdown selection multiple')%>
<% end %>
With this, I am able to create and save posts and the data is inserted in the database. However, when I try to edit an article, the pre-selected categories do not show. I have tried to set the value: #post.categories option in the select field and still cannot get the existing categories to show. Thanks in advance for your thoughts.
UPDATED
Models are as follows:
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories
end
class Category < ActiveRecord::Base
has_many :post_categories
has_many :posts, through: :post_categories
end
class PostCategory < ActiveRecord::Base
belongs_to :post
belongs_to :category, :counter_cache => :posts_count
end
Then my posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:edit, :update, :show]
def index
#posts = Post.all
end
def new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
flash[:notice] = "Post was successfully created"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not save your post"
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
flash[:notice] = "Post was successfully updated"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not update your post"
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :description, :published, :tag_list, category_ids: [])
end
def set_post
#post = Post.find(params[:id])
end
end

Rails Nested object Creationg

I have a project with the below scenario
Three Objects:
Client:The real world client
User: The who uses the portal
Department: The Client for whome the user works (XYZ.com,ABC.com)
The signup page have: FirstName,username,password,ClientName,Email
Client have only one field value: The client Name
FirstName,username,password,ClientName,Email are of user
Now what I want is to create a Department with ClientName as the Name of the Department and associate the user with the ID of the department.
User have a field department_id
Even with your reformulation, it is still not clear. So I'll go with my own interpretation. ALso, you didn't specify if you were using Mongoid or ActiveRecord. I'll go with a somewhat Mongoid style as it produces clean modelisation, but feel free to replace with ActiveRecord stuff.
Models
class User
has_one :department
has_one :client
accepts_nested_attributes_for :client
accepts_nested_attributes_for :department
field :first_name
field :password
field :username
field :email
class Client
belongs_to :user
field :name
class Department
belongs_to :user
field :name
your_controller.rb
def new
#user = User.new
#user.client = Client.new
#user.department = Department.new
end
def create
if #user = User.create(user_params)
redirect_to #user
else
render 'new'
end
end
private
def user_params
# Set department.name as client.name
if params[:user] and params[:user][:department] and params[:user][:client]
params[:user][:department][:name] = params[:user][:client][:name]
end
params.require[:user].permit(
:first_name, :password, :username, :email,
client_attributes: [:id, :name],
department_attributes: [:id, :name]
)
end
_form.html.erb
<%= form_for :user do |user| %>
<%= user.text_field(:name) %>
...
<%= user.fields_for :client do |client| %>
<%= client.text_field(:name) %>
<% end %>
<%= user.fields_for :department do |department| %>
<p class="text-info">A department will be created with the same name as the client</p>
<% end %>
<% end %>

Nested table isn't updated in rails 4

I am trying to update a nested form. The client table is updated successfully but the location table isn't update. Instead of its creating a new location. Do you guys have any solution ? I've already spend one day on it.
My models:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true, :update_only => true
end
class Location < ActiveRecord::Base
belongs_to :client
end
Selectcom::Application.routes.draw do
resources :clients
end
My Controller:
class ClientsController < ApplicationController
before_action :set_client, only: [:edit, :update]
def index
#clients = Client.paginate(page: params[:page])
end
def new
#client = Client.new
end
def create
#client = Client.new(client_params)
if #client.save
flash[:success] = "Client added successfully"
redirect_to clients_path
else
render 'new'
end
end
def edit
end
def update
if #client.update(only_client_params)
flash[:success] = "Job updated successfully"
redirect_to clients_path
else
render 'edit'
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
end
This is the client's edit.html.erb form
<%= form_for(#client, class: 'form-horizontal') do |f| %>
<%= render(partial: 'client_field', locals: {f: f}) %>
<%= f.fields_for :locations do |l| %>
<%= l.hidden_field :client_id, value: #client.id %>
<%= l.hidden_field :_destroy %>
<%= l.text_field :site, class: 'form-control' %>
<% end %>
<%= f.submit "Save", class: "btn lg-button" %>
<% end %>
Actually, you have two issues here. One in your Model and other in your Controller permitted params. Let's dig into them:
1) Model
The update_only option is ignored when used with collection association (it is your case), as said in Rails documentation:
For a one-to-one association, this option allows you to specify how
nested attributes are to be used when an associated record already
exists. In general, an existing record may either be updated with the
new set of attribute values or be replaced by a wholly new record
containing those values.
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include
the record's :id value. Otherwise a new record will be instantiated
and used to replace the existing one.
However if the :update_only option is true, the nested attributes are
used to update the record's attributes always, regardless of whether
the :id is present. The option is ignored for collection
associations.
So, the first step would be removing the update_only option of your Client class, because it will be ignored since you have a has_many association (collection association) with locations:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true
end
2) Controller
You have to permit the :id key for the :locations_attributes array. Since your update_only option in the model was ignored, Rails needs the param to tell that it is a record that exists and it's being updated rather than created.
You can solve your issue using the following in your client_params method (note the addition of the id key in your :locations_attributes key):
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:id, # Should be present; otherwise, Rails thinks that is a new record
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
I hope it helps !!

Rails upgradation from 3.2.x to 4.0.3 with Ruby 2

models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login
end
models/post.rb
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :user
attr_accessible :user_id, :description, :title
end
models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
attr_accessible :body, :user_id
end
I have installed 'strong_parameters'. And trying to make out with it. Can any one please guide me for model and controller code for the same.
Remove attr_accessible from models.
And in the controller, lets take the example of PostsController, create a private method post_params where you will define accessible attributes of Post model
class PostsController < ApplicationController
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post, notice: 'Post was successfully updated.'
else
render "edit"
end
end
private
def post_params
params.require(:post).permit(:user_id, :description, :title)
end
end

Nested attributes - Unpermitted parameters Rails 4

There is a lot of resources on SO about issues on Nested attributes in Rails 4 regarding strong parameters but I don't find any solution on this: (so sorry if it's a duplicate)
I have a 1-1 relation between member and profile.
When trying to update a member with profile attributes I've got this error:
Unpermitted parameters: profile
Where are my params
===> params: {"member"=>{"profile"=>{"first_name"=>"test", "last_name"=>"test"}, "email"=>"test#test.com"}}
My models:
Member.rb
class Member < ActiveRecord::Base
...
has_one :profile
accepts_nested_attributes_for :profile
end
Profile.rb
class Profile < ActiveRecord::Base
belongs_to :member
end
My form:
edit.html.slim
= simple_form_for [:admin, #member] do |f|
= f.simple_fields_for #member.profile do |pf|
= pf.input :first_name
= pf.input :last_name
= f.input :email
= f.button :submit
and my controller:
admin/members_controller.rb
class Admin::MembersController < Admin::BaseController
before_action :set_member, only: [:edit]
def edit
end
def update
if #member.update(member_params)
Rails.logger.debug "===> (1)"
redirect_to edit_admin_member_path
else
render action: 'edit'
end
end
private
def set_member
#member = Member.find(params[:id])
end
def member_params
params[:member].permit(:email, profile_attributes: [:first_name, :last_name ])
end
end
I've tried many things but don't understand where is my mistake.. Moreover in the update method it says the #member is correctly updated (shown ===> (1))
Ok get it..
I think this is caused by simple_form:
= simple_form_for [:admin, #member] do |f|
= f.simple_fields_for :profile, #member.profile do |pf|
= pf.input :first_name
= pf.input :last_name
= f.input :email
= f.button :submit
Try Adding the :member_id inside profile_attributes which is in member_params
so it will look like this:
def member_params
params[:member].permit(:email, profile_attributes: [:first_name, :last_name, :member_id ])
end