Nested form can't mass assign, create action - ruby-on-rails

I had to add the pen attributes to the paper model to stop the error "can't mass assign :pen", even thought I had the attr_accessible for pen_attributes.
Now, I'm getting a "unknown attribute: pen" error. It's pointing me to the second line of the create action. I can't figure it out.
I basically want to have to have the Paper New action create the pen and assign it to the paper.
Paper model
attr_accessible :name, :size, :line,
:pen_attributes,
:pen, :colour, :style
has_many :pens
accepts_nested_attributes_for :pens
Pens model
attr_accessible :name, :size, :line, :paper_attributes, :paper_id
belongs_to :paper
<%= simple_nested_form_for #paper do |f| %>
<%= f.input :name %>
<%= f.input :size, :placeholder => "text" %>
<%= f.input :line %>
<%= f.fields_for #pen do |h| %>
<%= h.input :pen, %>
<%= h.input :colour %>
<%= h.button :submit, :label => "create" %>
<% end %>
<% end %>
Paper Controller
def new
#user = current_user
#paper = #user.paper.build(params[:paper])
#pen = Pen.new(params[:pen])
end
def create
#user = current_user
#paper = #user.papers.build(params[:paper])
#pen = #paper.pens.build(params[:pen])
if #paper.save
flash[:notice] = "#{#paper.name} Created"
redirect_to(:action => "index")
else
flash.now[:notice] = "Error"
render 'new'
end
end
{"utf8"=>"✓",
"authenticity_token"=>"Z8vncB9ewDM1bWiKfsPHOGlkxcGpfhPjv0xpamudIIs=",
"paper"=>{"name"=>"three",
"size"=>"three",
"colour"=>"red",
"pen"=>{"colour"=>"test",
"pen"=>"test"}},
"commit"=>"Create"}

It looks like you have some minor discrepancies in your singular/plural naming.
I think you need to adjust the following:
<%= f.fields_for :pens, #pen do |h| %>
and probably:
attr_accessible :pens_attributes
as well as (possibly):
params[:pens]
Hope this helps, good luck!

Related

How to remember active_storage file name when validation fails in controller

In a rails 5.2.3 app, I have a model Post, which uses active_storage to attach a file and has fields for duration and place. The duration must be present.
class Post
has_one_attached :video
validates :duration, presence: true
end
Using simple_form
the fields in the form are declared as
<%= f.input :duration %>
<%= f.input :place %>
<%= f.input :video %>
The controller has the following logic for the create
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = 'Post has been saved'
redirect_to root_path
else
#title = 'New Post'
#user = current_user
render :new
end
end
private
def post_params
params.require(:post).permit(:duration, :video)
end
If the validation fails, the form shows value of place, but I lose the file name for the video. This means the user has to choose the file again. How do I fix this?
Following Thanh's suggestion, I did check this SO question, and tried changing the simple_form field to
<%= f.hidden_field :video, value: f.object.image.signed_id if f.object.video.attached? %>
<%= f.file_field :video %>
This remembered the file name, but did not display it. So I did the following work around:
<% if f.object.video.attached? %>
<span><strong>Video File Name:</strong> <%= f.object.video.blob.filename.to_s %>. To change, choose different file below:</span>
<% end %>
<%= f.hidden_field :video, value: f.object.image.signed_id if f.object.video.attached? %>
<%= f.file_field :video %>

form parameters sent but one of them received as nil in the controller

The purpose of the enroll model is to group three other models gymsite, user and role.
class Gymsite
has_many :users
class User
has_many :roles, through: :assigments
has_many :assigments
class Assigment
belongs_to :user
belongs_to :role
class Role
has many :assigments
has_many :users, through: :assigments
From the form at app/views/user/new.html.erb, input values are to be send to the app/controllers/user_controller.rb to perform the action 'enroll'. The problem is that all of the enroll_params are sent as is show in the browser, but at the controller, one of them become nil, in a recurrent way.
I have try by different means, including different form fields as select and check_boxes to try to solve this issue, with no result. I'm curios to learn what is the origin of the problem and how to solve it.Thanks
Browser processing information:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0z2B2t7H5wYJjtJMn8zBIPJ7sJaFk706EPh5JVZsC76x GWeggrJi7RSxAu8GJJmsyrNP7NzVv6rNvmumNDyhsg==", "enroll"=> {"user_first_name"=>"Lorena", "user_last_name"=>"Cueva", "user_email"=>"lcueva#example.com", "user_password"=>"[FILTERED]", "user_password_confirmation"=>"[FILTERED]", "gymsite_id"=>"1", "u_rol"=>["", "2", "4"]}, "commit"=>"Enroll User"}
app/views/users/new.html.erb
<%= simple_form_for #enroll, url: users_path do |form| %>
<div class='row'>
<div class='columns medium-2'></div>
<div class='columns medium-4'>
<%= form.input :user_first_name %>
<%= form.input :user_last_name %>
<%= form.input :user_email %>
<%= form.input :user_password %>
<%= form.input :user_password_confirmation %>
</div>
<div class='columns medium-4'>
<%= form.input :u_rol, as: :check_boxes, collection: Role.all.pluck(:name, :id), input_html: {multiple: true}%>
<%= form.submit 'Enroll User'%>
<% end %>
</div>
</div>
<%= link_to 'Back', users_path %>
at app/controllers/users_controller.rb
def new
#enroll = Enroll.new
#roles = Role.all
end
def create
#enroll = Enroll.new(enroll_params)
if #enroll.save
redirect_to root_path
else
render :new
end
end
def enroll_params
params.require(:enroll).permit(:user_email, :user_password, :user_password_confirmation, :gymsite_id, :u_rol, :user_first_name, :user_last_name)
end
at the app/models/enroll.rb all the parameters perform correct, except u_rol that becomes nil.
class Enroll
include ActiveModel::Model
attr_accessor :user_email, :user_password, :user_password_confirmation, :gymsite_id, :u_rol, :user_first_name, :user_last_name, :user, :role
def save
if valid?
#find gymsite by gymsite_id
gym = Gymsite.find(gymsite_id)
#save user for a gymsite
#user = gym.users.create(first_name: user_first_name, last_name: user_last_name, email: user_email, password: user_password, password_confirmation: user_password_confirmation )
#assign roles to the user
u_rol.each do |n|
rol = Role.find(n)
#user.roles << rol
end
end
end
I solve this issue changing enroll_parms to
def enroll_params
params.require(:enroll).permit(:user_first_name, :user_last_name,:user_email, :user_password, :user_password_confirmation, :gymsite_id, u_rol: [])
end
in the app/views/users/new.html.erb, added 'include_hidden:
false' to get rid of the blank value:
<%= form.input :u_rol, as: :check_boxes, collection: Role.all.pluck(:name, :id), include_hidden: false, input_html: {multiple: true}%>
Now it works!. Thks #jvillian for the tip!

Rails nested form attributes not getting saved

I've already looked through every other stackoverflow for this issue, but none of the solutions have fixed this. My elements in a nested_form are not being saved in the database. I've also made sure that all model associations are correct. I've been trying to fix this for nearly 8 hours now, and would really appreciate some help, especially considering every other solution hasn't worked.
Basically, I have a Playlist model that contains multiple Song models. I'm trying to use a nested_form to add the Song models to the Playlist. However, none of the Songs are ever being saved. I apologize if my methods are misguides, as I'm still fairly new to Rails.
GitHub Repo:https://github.com/nsalesky/Ultra-Music
playlists_controller.rb
def index
#user = current_user
#playlists = #user.playlists
end
def show
#user = current_user
#playlist = #user.playlists.find(params[:id])
end
def new
#playlist = Playlist.new
#I was told to do this
#playlist.songs.build
end
def create
#user = current_user
#playlist = #user.playlists.create(playlist_params)
if #playlist.save
redirect_to #playlist
else
render :action => 'new'
end
end
def edit
#playlist = current_user.playlists.find(params[:id])
end
def update
#user = current_user
#playlist = #user.playlists.find(params[:id])
if #playlist.update_attributes(playlist_params)
redirect_to #playlist
else
render :action => 'edit'
end
end
def destroy
#user = current_user
#playlist = #user.playlists.find(params[:id])
#playlist.destroy
redirect_to playlists_path(#user.playlists)
end
private
def playlist_params
params.require(:playlist).permit(:name, :description, songs_attributes: [:id, :name, :link, :_destroy])
end
playlist.rb
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, :allow_destroy => true, :reject_if => lambda { |a| a[:content].blank? }
validates :name, presence: true
validates_associated :songs, presence: true
_form.html.erb
<%= nested_form_for #playlist do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<!--<div>
<button type="button" id="addsong">Add Song</button><br>
<button type="button" id="removesong">Remove Song</button><br>
</div> !-->
<div>
<%= f.fields_for :songs do |song_form| %>
<%= song_form.text_field :name %>
<%= song_form.text_field :link %>
<%= song_form.link_to_remove "Remove Song" %>
<% end %>
<p><%= f.link_to_add "Add Song", :songs %></p>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
In your playlist.rb, you wrote:
:reject_if => lambda { |a| a[:content].blank? }
Here the block parameter |a| stands for attributes of a specific song. So a[:attribute] relates to a single attribute. The problem is your Song doesn't have a :content attribute. So this a[:content].blank? will always be true, means you would be rejected building a song.
Just change a[:content] to a valid attribute such as a[:name]

Rails autocomplete textfield won't work

Followed this guide by railscasts to setup autocompletion on collection_select.
https://www.youtube.com/watch?v=M7yhPlIehFA
In my example I'm trying to create a chatroom with a game related.
MODEL
belongs_to :game
validates :game, presence: true
def game_name
game.try(:name)
end
def game_name=(name)
self.game = Game.where(name: name).first_or_create if name.present?
end
CONTROLLER
def create
#room = current_user.chatrooms.build(room_params)
if #room.save
redirect_to #room
else
render 'new'
end
end
def room_params
params.require(:chatroom).permit(:title, :description, :game_id)
end
HTML
<%= simple_form_for #room do |f| %>
<p class="ftitle">Chatroom title</p>
<%= f.input :title, label: false %>
<p class="ftitle">Chatroom description</p>
<%= f.input :description, label: false %>
<p class="ftitle">Select related game</p>
<%= f.text_field :game_name, data: { autocomplete_source: Game.order(:name).map(&:name) } %>
<%= f.button :submit %>
<% end %>
It works fine until I try to create a chatroom with a game attached. It won't attach the game_id as a game. Not sure why.
Thanks.
Trying to link the game based on name seems quite brittle and open to future abuse.
However if that's really what you want to do, add :game_name to the .permit method in room_params.
It would be more robust to pass through an ID here, rather than plain text.

Ruby on Rails: Insert value into database based on user input?

I have an address column, but I'm not using :address in my form, instead, I have
:street, :state, :city, :zip columns:
<%= form_for #user, :html => {:multipart => true} do |f| %>
<%= f.label :street %>
<%= f.text_field :street %>
<%= f.label :state %>
<%= f.text_field :state %>
<%= f.label :city %>
<%= f.text_field :city %>
<%= f.label :zip %>
<%= f.text_field :zip %>
<%= f.submit %>
<% end %>
Because I don't have a field for :address, but I would like to compile the information in the form and still insert it into my database. For example
(12345 Maple St, Made City CA 90017 or maybe when I get more advanced, I'll use some gem to compile the given information and put into a correct address format.)
How do I do such a thing?
My controller looks something like this:
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user, notice: "Successfully created."
else
render :action => 'edit'
end
end
def create
address = [params[:user].delete(:street), params[:user].delete(:state), params[:user].delete(:city), params[:user].delete(:zip)].join(", ")
#user = User.new(params[:user].merge(:address => address))
if #user.save
redirect_to #user, notice: "Successfully created."
else
render :action => 'edit'
end
end
Another option would be to do it in model
attr_accessor :street, :city, :state, :zip
before_create :concate_address_attrs
Edit:-
I feel before_save is better to use over before_create
before_save :concate_address_attrs
def concat_address_attrs
self.address = [street, city, state, zip].join(", ")
end
Unless processing overhead is a major concern, you're breaking Rails' DRY principles by saving concatenated model attributes as a separate attribute.
The Rails Way of accomplishing this would be to create a model convenience method that concatenates existing attributes into a pseudo-address attribute without requiring any additional attributes be committed to the database:
# my_model.rb
def address
[street, city, state, zip].join(', ')
end
# my_view.html.erb
<%= #user.address %>
Alternatively, if you only need the full address for display in your views, you might consider using a view helper:
# app/helpers/my_model_helper.rb
module MyModelHelper
def formatted_address(address)
[address.street, address.city, address.state, address.zip].join(', ')
end
end
# my_view.html.erb
<%= formatted_address(#address) %>

Resources