Rails cant assign attributes for new entity - ruby-on-rails

Can someone tell me why entity always creates with name == nil :
in ProductsController:
def create
#product = Product.new(name: params[:product][:name])
byebug
if #product.save
redirect_to users_path
end
end
in view :
<%= form_for Product.new do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
model:
class Product < ApplicationRecord
has_many :categories
attr_accessor :name
end

Can someone tell me why entity always creates with name == nil
Because of your attr_accessor. It overwrites the auto-created methods from active record (the ones that know about persistence). Just remove it.
(assuming that your table products has column name. If it doesn't, create a migration to add it.)

You'll need to ensure you're using strong params for this, otherwise the params won't pass in as expected i.e.
def create
#product = Product.new(product_params)
byebug
if #product.save
redirect_to users_path
end
end
private
def product_params
params.require(:product).permit(:name)
end
More info can be found here: http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters

Name is not saved in database because you have used name as attr_accessor.
Create name column in product table. If you already have name column then remove attr_accessor :name from product.rb model.

Related

undefined method `commentable' for #<ActiveRecord::Associations::CollectionProxy []> (accesing commentable id and type via polymorphic association)

I tried to fix this issue for a day but cannot find solution.
What I am doing now is to render the comment form
<%= form_for #comment, remote: true do |form|%>
<%= form.hidden_field :question_id, value: #question.id%>
<%= form.hidden_field :commentable_id, :value => #question.comments.commentable.id %>
<%= form.hidden_field :commentable_type, :value => #question.comments.commentable.class.name %>
<% end %>
I just dont know how to access the commetable_id and type that was created with the comment controller to record the id of the comment and the type of the commenting model (e.g. client or lawyer)
# GET /questions/1 or /questions/1.json
def show
set_question
#comment = Comment.new
#comments = Comment.all
end
# GET /questions/1 or /questions/1.json
def show
set_question
#comment = Comment.new
#comments = Comment.all
end
resources :questions do
resources :comments do
end
end
As the error message says, you can't call commentable on #question.comments - an association. Pass an array to the form_for method with the commentable (Question) object and the comment object. You don't need to set any hidden fields.
<%= form_for [#question, #comment] do |f| %>
<div><%= f.label :content %></div>
<div><%= f.text_area :content %></div>
<div><%= f.submit 'Post' %></div>
<% end %>
Replace content with the field where you're storing the comment's content.
This should generate a form tag with an action attribute of /questions/1/comments which upon submission is processed by CommentsController#create.
class CommentsController < ApplicationController
before_action :set_commentable
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
redirect_to #commentable, notice: 'Comment created'
else
render :new
end
end
private
def set_commentable
# e.g. request.path => '/questions/1/comments'
resource, id = request.path.split('/')[1, 2] # ['questions', '1']
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:content)
end
end
In the set_commentable method, the commentable type and its id are detected from the request path. Since resource is 'questions', resource.singularize.classify.constantize returns the Question model. The commentable object is then found using the find method. The CommentsController#create method creates the comment and redirects to the commentable object which is the question show page (/questions/:id). If there's an error, it renders the new view (you have to create views/comments/new.html.erb to render the form with errors).
You are using question.comments which indicates has many relation, So it will return you an active record association array, So if you want to find the commentable id, you need to take single record from the array. For example if you want first comment commentable id, then use
#question.comments.first.commentable.id
If each comment has different commentable id, you need to iterate through loop.
If you have some factor to apply condition, then apply the condition
#question.comments.where('your condition').first.commentable.id
If you use above code, still you will get the array, so I have used .first. Use a uniq value in where condition, so you will have only single record with that value.

Creating form_for for model association

I have 2 models:
brand
has_many :products
product
belongs_to :brand
In my view (show_brand.html.erb), i displaying all information about brand using #brand.name ....
I want to create form for product that belongs_to brand i'm displaying information about.
Something like:
form_for(#brand.products) ...
How can i preform that?
How can i attach user_id to product form (product belongs_to user) without adding it in controller manually
NOTICE:
About first item in my list, i know that it can be done by upgrading routes to nested and passing array with main object and association object. But if there is another way of doing that? Without modifying routes.rb and ...
You can use accepts_nested_attributes_for.
#brand_controller.rb
def new
#brand = Brand.new
#product = #brand.products.build
end
def create
#brand = Brand.new(brand_params)
if #brand.save
.....
else
.....
end
end
private
def brand_params
params.require(:brand).permit(:id, brand_attribute_1, brand_attribute_2, products_attributes: [:id, :product_attribute_1, :user_id, :product_attribute_2])
end
In your form
<%= form_for #brand do |f| %>
----code for brand attributes ---
<%= f.fields_for #product do |p| %>
----code for product attributes----
<%= p.hidden_field user_id, :value => current_user.id %> #to attach user_id to product
<% end %>
<%= f.submit "Submit" %>
<% end %>
For question 1, you can use "nested form".
Please check below link.
http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast
For question 2, even though you set user_id in "product form", you still have to do some check in your controller/model in case any undesired value is set to user_id. So the better way is you set it yourself at backend.

Type error when creating record with strong parameters

I have the following controller action
def create
#user= user.new(user_params)
...
end
private
def user_params
params.require(:user).permit(:username, :device)
end
the user model holds a foreign key to the devices table as such
class user < ActiveRecord::Base
belongs_to :device
end
device has a has_many attribute on user also.
when I attempt to create a new user with form details
user[username] = 'test' and user[device] = 1
I get a type error ActiveRecord::AssociationTypeMismatch Device(#37560060) expected, got String(#15955200)
Use form_for in your new user template and pass the new #user object to it and also define input attributes like below
<%= form_for #user, create_user_path do |f| %>
<%= f.text_field :username %>
<%= f.text_field :device_id %>
<%= f.submit %>
<% end %>
and also change your strong parameter :device to :device_id as pragma told above
Change 'device' to 'device_id'
def user_params
params.require(:user).permit(:username, :device_id)
end
...
user[device_id] = 1

"One-to-Many" Rails Form Creation

I have an association of One Classroom has Many Students. I want to create a form where I can create a student and assign him a classroom. And I am having problems creating the form.
model/classroom.rb
class Classroom < ActiveRecord::Base
has_many :students
end
model/student.rb
class Student < ActiveRecord::Base
belongs_to :classroom
end
I want to create a new student and assign it to a certain classroom.
<%= form_for(#student) do |f|%>
<%= f.label :name %>
<%= f.text_field :name %>
<br />
<br />
<%= f.label :student.classroom.number %> #Is this correct?
<%= f.text_field :student.classroom.number %> #Is this correct?
<%= f.submit %>
<%end%>
The attributes for each model are
1.9.3-p448 :026 > Classroom
=> Classroom(id: integer, number: string, created_at: datetime, updated_at: datetime)
1.9.3-p448 :027 > Student
=> Student(id: integer, name: string, created_at: datetime, updated_at: datetime, classroom_id: string)
students_controller
class StudentsController < ApplicationController
def index
#students = Student.all
end
def show
#student = Student.find(params[:id])
end
def new
#student = Student.new
end
def create
#student = Student.new(article_params)
respond_to do |format|
if #student.save
format.html {redirect_to(#student, notice: 'Student was successfully created.')}
else
format.html {render action: "new"}
end
end
end
private
def article_params
params.require(:student).permit(:name, :classroom_id)
end
end
classroom_controller
class ClassroomsController < ApplicationController
def index
#classrooms = Classroom.all
end
def show
#classroom = Classroom.find(params[:id])
end
def new
#classroom = Classroom.new
end
def create
#classroom = Classroom.new(article_params)
respond_to do |format|
if #classroom.save
format.html {redirect_to(#classroom, notice: 'Classroom was successfully created.')}
else
format.html {render action: "new"}
end
end
end
private
def article_params
params.require(:classroom).permit(:number)
end
end
You can set a hidden field setting it to the classroom itself, if you already know which classroom you want to add him in:
<%= f.hidden_field, :classroom_id, value: here_you_put_the_classroom_id $>
And don't forget to add :classroom_id in the permitted params in your controller.
Another way you can do if you want the option to select the classroom you are putting the student in, you can create a select field passing all the classrooms.
<% classroom_array = Classroom.all.map { |classroom| [classroom.name, classroom.id] } %>
<%= options_for_select(classroom_array) %>
Don't forget to add the permitted params again.
Hope it helps.
***UPDATE***
The options_for_select should go inside de select tag, like this:
<% classroom_array = Classroom.all.map { |classroom| [classroom.number, classroom.id] } %>
<%= f.label :classroom %>
<%= f.select(:classroom_id, options_for_select(classroom_array)) %>
***UPDATE 2***
Pluck could also be an option, as long as you pass the classroom id as param. So, the code can be refactored to:
<%= f.label :classroom %>
<%= f.select :classroom_id, Classroom.all.pluck(:name, :id) %>
Assuming you want to assign the Student to an existing Classroom:
First, ensure the Student model has the following in the attr_accessible:
attr_accessible :classroom_id
In your form, instead of your second label/text_field, you should then be able to do:
<%= f.label :classroom %>
<%= f.select(:classroom_id, Classroom.all.pluck(:number)) %>
Note that for the f.select method you must pass the attribute you are setting, not the association name (i.e., classroom_id not classroom)
Also note that best practice would be to move logic associated with collecting information from a model (i.e. Classroom.all.pluck(:number)) into an instance variable in the controller,
e.g. #classrooms = Classroom.all.pluck(:number)
and using that #classrooms instance variable in your view instead.
Aside from the above, you should also read some more about symbols. What you've tried there with :student.classroom.number isn't going to work how you thought it might. There's a good SO question about it here: When to use symbols instead of strings in Ruby?

Can't get my type attribute set in my Profile model for STI

I have a User model which has_one :profile, and the profile model has a type column for Single Table Inheritance. I want a user to set type upon signing up, and I'm having trouble doing this.
i'm trying this in my profiles controller:
def create
#profile = Profile.find(params[:id])
type = params[:user][:profile_attributes][:type]
if type && ["Artist","Listener"].include?(type)
#profile.update_attribute(:type,type)
end
end
and this in my form for the User new view:
<%= form_for(setup_user(#user)) do |f| %>
...
<%= f.fields_for :profile do |t| %>
<div class ="field">
<%= t.label :type, "Are you an artist or listener?" %><br />
<p> Artist: <%= t.radio_button :type, "Artist" %></p>
<p> Listener: <%= t.radio_button :type, "Listener" %></p>
</div>
<% end %>
...
<% end %>
and in my application helper:
def setup_user(user)
user.tap do |u|
u.build_profile if u.profile.nil?
end
end
I can't seem to get type set when a User is created. It still defaults to nil. Why is this and how can I accomplish it? I'd appreciate a code example.
UPDATE:
This is the relevant code in my User model:
has_one :profile
accepts_nested_attributes_for :profile
before_create :build_profile
UPDATE 2: I get this error: WARNING: Can't mass-assign protected attributes: type
It looks like the object isn't being saved to the database. Try something like this:
def create
#profile = Profile.find(params[:id])
type = params[:user][:profile_attributes][:type]
if type && ["Artist","Listener"].include?(type)
#profile.update_attribute(:type,type)
end
end
Your last issue can be resolved by adding
attr_accessible :type
"type" is a protected attribute and you can't mass-assign a protected attribute.
The column 'type' is reserved for storing the class in case of inheritance. Try renaming the table column to something like "modelname_type".
Replace your create method with this one:
def create
profile_type = params[:user][:profile][:type].constantize
if ["Artist", "Listener"].include? profile_type
#profile = current_user.profile = profile_type.new
current_user.save!
else
flash[:alert] = "Profile type not supported"
end
end
UPDATE: This is not really necessary. Useful code perhaps, but not necessary as a solution for the above problem.

Resources