I have two models item and user_item. Item has many user_items and user_items belongs to item. I have a form where a user can create a new item. In the form a user should include a picture. The name, description, and tags get saved to a new item object. The picture should be saved as an attribute on a user_item object which gets created at the same time.
I've been reading about nested model forms and strong parameters and using accepts_nested_attributes_for. My form seems to be working but I don't understand why I don't need accepts_nested_attributes_for. Although it seems to be working, is there a problem with the way I'm doing it?
Form
<%= simple_form_for @item, url: items_path, method: :post do |item_builder| %>
<div class="well">
<%= item_builder.input :name, required: false, error: false, label: "Item name" %>
<%= item_builder.input :description, as: :text, required: false, error: false, label: "Describe item" %>
<%= item_builder.input :tag_list, required: false, label: "Tags" %>
<%= item_builder.simple_fields_for @item, @item.user_items.build do |user_item_builder| %>
<%= user_item_builder.input :picture, as: :file, required: false, label: "Picture of you with this item" %>
<% end %>
</div>
<div class="clearfix">
<%= item_builder.submit 'Submit new item', class: "btn btn-primary pull-right inherit-width" %>
</div>
<% end %>
Items_controller
def create
Item.transaction do
@item = Item.create(name: item_params[:name],
description: item_params[:description],
tag_list: item_params[:tag_list],
created_by: current_user.id,
status: Item::STATUS[:pending])
if item_params[:item] == nil
@item.errors.add(:picture, "is required")
end
if @item.errors.empty?
@user_item = @item.user_items.build(user_id: current_user.id, picture: item_params[:item][:picture])
if @user_item.save
flash[:notice] = "Thank you for your item submission."
redirect_to items_path
else
render :new
raise ActiveRecord::Rollback, "Useritem create failed"
end
else
render :new
raise ActiveRecord::Rollback, "no picture"
end
end
end
private
def item_params
params.require(:item).permit(:name, :description, :tag_list,
item: :picture)
end
EDIT:
I have made changes going along with Richard's answer below but I am now running into a couple problems. This is my current create action in the items_controller
def create
@item= Item.new item_params
@item.status = Item::STATUS[:pending]
@item.created_by = current_user.id
@item.user_items.first.user_id = current_user.id
if @item.save
redirect_to items_path, notice: "Thank you for your item request!
else
render :new
end
end
On validation fail I was getting a missing template error so I added an
elseandrender :new.Now on validation fail the new template is rendered but the fields_for input for picture is not shown.
I have
validates_presence_of :picturein user_item.rb and I havevalidates_associated :user_itemsin item.rb but I am still able to submit the form without a picture.As you can see on valid submission I need the item's status attribute to be pending and a created_by attribute to be the current user id. I also needed the user_item's user_id attribute to be set to the current user id. I set this in the controller. I am wondering if setting this like I am in the create action is the correct way.
simple_fields_for @item, @item.user_items.buildis just strange - just pass it the name of the association you want to create fields for. Also for read the guide for whitelisting nested params: guides.rubyonrails.org/…