Only Last Parameter being passed into Object - ruby

My app is supposed to create an image object for each image selected in the input field of my form. It–the create action– iterates over the :picture param and for each entry, it is supposed to create a new image object. However, it seems to only create an image object for the last image selected in the input box. Why is this not functioning properly?
Controller
class Admin::ImagesController < ApplicationController
respond_to :html, :json
#before_filter :split_hash, :only => [ :create, :update ]
def index
#album = Album.find(params[:album_id])
#images = #album.images.all
end
def new
#album = Album.find(params[:album_id])
#image = #album.images.new
end
def create
params[:image][:picture].each do |image|
#album = Album.find(params[:album_id])
#params = {}
#params['picture'] = image
#image = #album.images.build(#params)
end
if #image.save
flash[:notice] = "Successfully added image!"
redirect_to [:admin, #album, :images]
else
render "new"
flash[:notice] = "Did not successfully add image :("
end
end
def show
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
end
def edit
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
end
def update
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
if #image.update_attributes(params[:image])
flash[:notice] = "Successfully updated Image"
redirect_to [:admin, #album, :images]
else
render "edit"
end
end
def destroy
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
#image.destroy
#albumid = #album.id
#id = #image.id
FileUtils.remove_dir("#{Rails.root}/public/uploads/image/picture/#{#albumid}/#{#id}", :force => true)
redirect_to admin_album_images_path(#album)
end
# def split_hash
# #album = Album.find(params[:album_id])
# #image = #album.images
# array_of_pictures = params[:image][:picture]
# array_of_pictures.each do |pic|
# size = array_of_pictures.size.to_i
# size.times {#image.build(params[:image], :picture => pic)}
# #image.save
# end
# end
end
View Form
<%= form_for([:admin, :album, #image], :html => {:multipart => true}) do |f| %>
<%= f.hidden_field :album_id, :value => #album.id %>
<%= f.file_field :picture, :multiple => true %>
<%= f.submit "Submit" %>
<%end%>
Request Params on Submit
{"image"=>{"picture"=>[#<ActionDispatch::Http::UploadedFile:0x10c986d88 #tempfile=#<File:/var/folders/bx/6z1z5yks56j40v15n43tjh1c0000gn/T/RackMultipart20130404-53101-3c2whv-0>,
#headers="Content-Disposition: form-data; name=\"image[picture][]\"; filename=\"background-pic.jpg\"\r\nContent-Type: image/jpeg\r\n",
#content_type="image/jpeg",
#original_filename="background-pic.jpg">,
#<ActionDispatch::Http::UploadedFile:0x10c986d60 #tempfile=#<File:/var/folders/bx/6z1z5yks56j40v15n43tjh1c0000gn/T/RackMultipart20130404-53101-bvdysw-0>,
#headers="Content-Disposition: form-data; name=\"image[picture][]\"; filename=\"bible-banner.png\"\r\nContent-Type: image/png\r\n",
#content_type="image/png",
#original_filename="bible-banner.png">],
"album_id"=>"10"},
"authenticity_token"=>"dr8GMCZOQo4dQKgkM4On2uMs8iORQ68vokjW0e4VvLY=",
"commit"=>"Submit",
"utf8"=>"✓",
"album_id"=>"10"}
I would greatly appreciate any help you can share!

#album.images.build will only create the object, it will not save it to the database. When you exit your each loop, #image is just the last image that was built (which would be the last image in the params).
Using #album.images.create will actually save the image to database so that it is persisted like you expect; however, your logic after the each block won't be valid anymore, since you'll only be verifying that the last image was saved. You should check if each image is saved inside of the each block, and somehow record which ones were unsuccessful if that is what you want to do.

Related

render :new is not working anymore when I'm using services

I'm trying to learn about services in Rails in order to avoid fat controllers.
Here a tried a simple product review creation
class ProductReviewsController < ApplicationController
before_action :set_product, only: :create
def new
#product_review = ProductReview.new
#product = Product.find(params[:product_id]) end
def create
if AddReviewService.call(product_review_params, #product)
redirect_to product_path(#product)
else
render :new
end
end
private
def set_product
#product = Product.find(params[:product_id]) end
def product_review_params
params.require(:product_review).permit(:content, :rating) end end
The thing is that in the case of wrong parameters for the review the render :new generates the following error :
screenshot of error received
Showing /home/miklw/code/michaelwautier/multitenant_app/app/views/product_reviews/new.html.erb where line #3 raised:
undefined method 'model_name' for nil:NilClass
Extracted source (around line #3):
1 <h1>New review for <%= #product.name %></h1>
2
3 <%= simple_form_for [#product, #product_review] do |f| %>
4 <%= f.input :content %>
5 <%= f.input :rating %>
6 <%= f.button :submit %>
Rails.root: /home/miklw/code/michaelwautier/multitenant_app
Application Trace | Framework Trace | Full Trace
app/views/product_reviews/new.html.erb:3:in `_app_views_product_reviews_new_html_erb__802305224391576972_70155240081100'
app/controllers/product_reviews_controller.rb:14:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"Z7YeYvXKKr7uYhANlevZ4H8U00p9givKzXzfue4pNFk0jE2DtTNY7Eacqati+V8IihSofLc2WPa4ZBzR2o0v5w==",
"product_review"=>{"content"=>"te", "rating"=>"9"},
"commit"=>"Create Product review",
"product_id"=>"4"}
Toggle session dump
Toggle env dump
Response
Headers:
None
In the error page console, if I type #product, I get the expected product object, but #product_review is nil.
BUT, if I use the regular way (see below), the form gets re-render as it should, with the notice message of the form
class ProductReviewsController < ApplicationController
before_action :set_product, only: :create
def new
#product_review = ProductReview.new
#product = Product.find(params[:product_id])
end
def create
#product_review = ProductReview.new(product_review_params)
#product_review.product = #product
if #product_review.save
redirect_to product_path(#product)
else
render :new
end
end
private
def set_product
#product = Product.find(params[:product_id])
end
def product_review_params
params.require(:product_review).permit(:content, :rating)
end
end
Any idea what could cause this issue ?
EDIT : Here is the service I call :
class AddReviewService < ApplicationService
attr_reader :content, :rating
def initialize(params, product)
#content = params[:content]
#rating = params[:rating]
#product = product
end
def call
review = ProductReview.new(content: #content, rating: #rating)
review.product = #product
return true if review.save
return false
end
end
EDIT 2 : returning the review when saved
def call
review = ProductReview.new(content: #content, rating: #rating)
review.product = #product
return review if review.save
return false
end

Get value out of static rails select_tag

I have just started out programming with ruby on rails. I really like it, but sometimes it's really complicated. What I am trying to do is to get the selected value out of the select_tag and pass it to the Model where I will multiply the value to another one (that comes from an from_for textfield).
The problem is I wasn't able to figure out how to get the value from the View to the Controller and then to the Model.
Here is my code:
View:
<%= label_tag 'Remind' %>
<%= f.number_field :remind %>
<%= select_tag :select_conv, options_for_select([['Day', 1], ['Week', 7], ['Month', 30]]) %>
Controller:
def create
add = Item.new(item_params)
if add.save
flash[:notice] = ''
redirect_to items_path
else
redirect_to new_item_path
flash[:error] = ''
end
private
def item_params
params.require(:item).permit(:itemname, :amount, :bbf, :remind)
end
end
Model:
def convert_to_d
convert = self.remind * self.v_convertor
self.assign_attributes(remind: convert)
end
Thank you in advance
You have to do some changes:
View:
<%= label_tag 'Remind' %>
<%= f.number_field :remind %>
<%= select_tag :select_conv, options_for_select([['Day', 1], ['Week', 7], ['Month', 30]]) %>
From the View, it will return a hash with the values of each user's input. So, for this example, it will return:
params = { remind: user_input, select_conv: user_input }
You can catch that in your controller with the method item_params, but
you have to specify the parameters that you want in your method, so your item_params should be:
Controller:
def create
add = Item.new(item_params)
if add.save
flash[:notice] = ''
redirect_to items_path
else
redirect_to new_item_path
flash[:error] = ''
end
end
private
def item_params
params.require(:item).permit(:itemname, :amount, :bbf, :remind, :select_conv) # << update here
end
In your model, you can access the values saved in item_params with their names, as you did with self.remind, you can call it with self.select_conv.
Model:
# self.select_conv can be used now.
def convert_to_d
convert = self.remind * self.v_convertor
self.assign_attributes(remind: convert)
end
You can also use some validations in your model to guarantee integrity from the user's data. For more information about validations.

Rails: Submitting an array of child elements

I'm trying to submit a form which is supposed to have an array of child elements. I'm not sure if I have it correctly. Can somebody help me refactor?
I have a couple of models user and item. A userhas_many :items.
The form that sends the information for user and items look like the following -
<%= form_for(#user) do |f| %>
<% f.text_field :name %>
<% f.fields_for :items do |item| %>
<% item.text_field :name %>
<% item.text_field :price %>
<% end %>
<% end %>
I have the following in my controller -
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
#items = #user.items.build(item_params)
if #items.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
end
format.html { redirect_to #user, notice: 'User was successfully created. But there was a problem saving the items.' }
else
format.html { render action: 'new' }
format.json { render json: #userd.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:name)
end
def item_params
params.require(:user).require(:item).permit(:name, :price)
end
When I save the user, only 1 record of items get saved, meaning - the data isn't being passed as an array of objects. How do I get this to work?
It looks like right now you are only ever telling Rails to build one item object. Your form is passing up the two parameters for an item, and in your controller you call build once, so #items is only ever set equal to the single Item returned by build. Unless their are hidden parts of your form you aren't showing, it looks like you're only setting one set of options for a user's items. If your goal is to have an array of many items with identical parameters, the fastest fix would be to just do this within the logic of your create action. There are many ways you could implement that (here is one simple way):
#items = []
x.times { #items << #user.items.build(item_params) }
If you're looking to send a request from the form with multiple unique items, you'll have to add more fields to your form, since currently you are only sending one set of parameters.
There are few corrections to be made in your code.
In your users_controller
def new
#user = User.new
3.times do #this will generate three records of items
#user.items.build
end
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #userd.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:name,items_attributes: [:name,price])
end

How to use last created forms values for new form?

I have the follow models:
User
has_many :armies
Army
belongs_to :user
My controller with the added current_user:
class ArmiesController < ApplicationController
before_filter :authenticate_user!
def new
#army = Army.new
end
def create
#army = current_user.armies.new(params[:army])
respond_to do |format|
if #army.save
format.html { redirect_to new_army_path, :notice => "New army added" }
else
format.html { render :new }
end
end
end
end
I want to use my last created forms value for the new one. We'll use my strength field as an example:
<%= form_for #army do |f| %>
<%= f.label :strength, "Army Strength" %>
<%= f.text_field :amount %>
<%= f.submit "Create" %>
<% end %>
How can I save the value that the user input's in the strength field so it remains on the form after the last form is created?
EDIT:
def new
#army = Army.new(strength: session[:last_army_strength],
type_id: session[:last_type])
end
def create
#army = current_user.armies.new(params[:army])
session[:last_army_strength] = params[:army][:strength]
session[:last_type] = params[:army][:type_id]
respond_to do |format|
if #army.save
format.html { redirect_to new_army_path, :notice => "New army added" }
else
format.html { render :new }
end
end
end
end
I think this should work:
class ArmiesController < ApplicationController
before_filter :authenticate_user!
def new
#army = Army.new(strength: session[:last_army_strength])
end
def create
#army = current_user.armies.new(params[:army])
session[:last_army_strength] = params[:army][:strength]
respond_to do |format|
if #army.save
format.html { redirect_to new_army_path, :notice => "New army added" }
else
format.html { render :new }
end
end
end
end

Rails 3 render partial outside view?

I am working on a multisite for a client for a skateboarding website. So far everything is great but I am starting to get stuck on the whole partial thing. I have a site and site has_many :albums(Album also belongs to site) but when I try to render albums from a site on the sites homepage i get undefined method `model_name' for NilClass:Class?
I have am trying to render albums/_album.html.erb on the sites/show page to display a site latest's album on the homepage of the site.
Albums Controller
class AlbumsController < ApplicationController
def index
#albums = Album.all
end
def show
#album = Album.find(params[:id])
end
def new
#album = Album.new
end
def edit
#album = Album.find(params[:id])
end
def create
#album = current_site.albums.build(params[:album])
if #album.save
redirect_to albums_path, :notice => 'Album was successfully created.'
end
end
def update
#album = Album.find(params[:id])
if #album.update_attributes(params[:album])
redirect_to album_path(#album), :notice => 'Album was successfully updated.'
end
end
def destroy
#album = Album.find(params[:id])
#album.destroy
end
end
Sites Controller
class SitesController < ApplicationController
def index
#sites = Site.all
end
def show
#site = Site.find_by_subdomain!(request.subdomain)
end
def new
#site = Site.new
end
def edit
#site = Site.find(params[:id])
end
def create
#site = Site.new(params[:site])
if #site.save
redirect_to #site, :notice => 'Signed up!'
end
end
def update
#site = Site.find(params[:id])
if #site.update_attributes(params[:site])
redirect_to #site, :notice => 'Site was successfully updated.'
end
end
def destroy
#site = Site.find(params[:id])
#site.destroy
end
end
Site Show.html
<p id="notice"><%= notice %></p>
<p>
<b>First name:</b>
<%= #site.first_name %>
</p>
<p>
<b>Last name:</b>
<%= #site.last_name %>
</p>
<p>
<b>Subdomain:</b>
<%= #site.subdomain %>
</p>
<%= render :partial => 'albums/album'%>
<%= link_to 'Edit', edit_site_path(#site) %> |
<%= link_to 'Back', sites_path %>
Albums/_album.html.erb
<%= div_for #album do %>
<h2><%= #album.title %></h2>
<%= image_tag #album.photo.url(:small) %>
<% end %>
Am I missing something in my albums controller?
In your show.html, you need to pass in the collection of albums to the render method
<%= render :partial => 'albums/album', :collection => #site.albums %>
Within the _album.html.erb partial, you need to reference the album attribute as a local attribute, like so
<%= div_for album do %>
<h2><%= album.title %></h2>
...
You can read more about partials here 3.4.5 Rendering Collections

Resources