When I am creating a skid, I am trying to get a value that user has entered so that I can create that record that many times.
in _form.html.erb is where all my code sits. and in the new.html.erb is where I call the form with:
<%= render 'form' %>
Here is the piece of code from form that I am trying to access:
<%= f.label :skid_count %>
<%= f.number_field :skid_count, :value => '1', :required => 'required', :pattern => ValidationValues.c_integer, :placeholder => ValidationValues.p_integer %>
In the controller I am trying to do this:
def create
#skid = Skid.new(params[:skid])
count = params[:skid_count].to_i
# Create record in the database, and return an appropriate message
respond_to do |format|
if #skid.save
for i in 1..count
Skid.new(params[:skid]).save
end
format.html { redirect_to #skid, notice: 'Skid was successfully created.' }
format.json { render json: #skid, status: :created, location: #skid }
else
format.html { render action: "new" }
format.json { render json: #skid.errors, status: :unprocessable_entity }
end
end
end
for some reason the count variable is not picking up the number, if I hard code it and put 3 in there, it would create the record 4 times just as intended, however if I try to get the numeric value based on what user entered, as shown above, it doesn't works. It creates just 1 record every time.
Is there a reason why I cannot access that param?
It's look like you are setting the variable before the record saves, so there is no record to set the variable with at that point. If you move the line down a few spaces, it should work.
def create
#skid = Skid.new(params[:skid])
# Create record in the database, and return an appropriate message
respond_to do |format|
if #skid.save
count = params[:skid_count].to_i # if the record saves, create variable with the new params
for i in 1..count # do your magic
Skid.new(params[:skid]).save
end
I have solved this question by getting the value from the attribute in this manner:
count = params[:skid]["skid_count"]
I hope this helps somebody else stumbled with the same problem.
I will expand on your answer to explain why.
As you are building the field via the form_for helper, it automatically scopes it under the model attributes in the param hash.
params[:skid][:skid_count] would work as well
if you want :skid_count to be outside of the params hash (as to not to trigger forbidden attributes in newer versions of rails, you can build it by just using number_field_tag(:skid_count) which would them be available to your controller as params[:skid_count]
Related
In a Rails 3.2 app, I have a validation for an attachment type.
Attachment model:
class Attachment < ActiveRecord::Base
validates_presence_of :name
validates_attachment_presence :attach, :message => "No file selected"
validate :check_type
def check_type
if self.costproject_id != nil
if self.attach_content_type != 'application/pdf'
self.errors.add(:pdf, " ONLY")
return false
end
end
end
But, the return false sends me to this URL:
http://localhost:3000/attachments
I want it to go back to the previous input screen:
http://localhost:3000/attachments/new?costproject_id=2
How do I accomplish that?
Thanks!!
UPDATE1
Perhaps the redirect has to take place in the controller?
format.html { render action: "new" }
Attachment controller:
# POST /attachments
# POST /attachments.json
def create
#attachment = Attachment.new(params[:attachment])
respond_to do |format|
if #attachment.save
format.html { redirect_to session.delete(:return_to), notice: 'Attachment was successfully created.' }
format.json { render json: #attachment, status: :created, location: #attachment }
else
format.html { render action: "new" }
format.json { render json: #attachment.errors, status: :unprocessable_entity }
end
end
end
I changed this line:
format.html { render action: "new" }
To:
format.html { redirect_to request.referer }
And now it goes back to where I want. But, I've lost the errors - they don't display.
To help you understand what's going on here. When you go to /attachments/new you are rendering a form. When you press submit, you are sending a POST request to /attachments, which invokes the create action.
You're create action appears to be solid and idomatic. However when you render action: "new" in the case of an error, it's not a full redirect, it's rendering the form in the context of the current action.
Normally this is fine, because idomatic rails would have you building a single, very similar, model object in both new and create, and the form for helper would render that object. However your new action is creating all kinds of objects based on a large assortment of query parameters, which I'm guessing is why you are seeing behavior you don't like.
I expect your final solution will involve bringing all those parameters into Attachment in some way, if they don't need to be saved to the database, you can make attr_accessors on Attachment
# Model
class Attachment < ActiveRecord::Base
attr_accessor :worequest_id, :workorder_id # etc
end
# View
<%= form_for #attachment do |f| %>
<%= f.hidden :worequest_id %>
<% end %>
Approaching it this way, your post request params will look like
{
attachment:
{
worequest_id: 1,
# etc
}
}
And you would also need to rework your query params to nest the inidividual ids inside of an attachment
/attachments/new?[attachment][worequest_id]=1
This way you could build attachment from params in both actions:
Attachment.new(params[:attachment])
And now your current create action should more or less work as expected, because now it's idomatic rails.
You still aren't going to get the new action with the same query params, but since you are taking those params and filling them in hidden fields on the form, they won't be lost when you try and fail to create. In any case, unless you do something to persist the values between requests, the POST to /attachments is going to wipe out the ery params.
Try this.
Replace
return false
With
redirect_to request.referrer || root_url
Note: root_url here is a catchall. Also this is Rails 4, I do not know if it also applies to Rails 3. Worth a try, though.
Debug ideas
First confirm a simple redirect_to root_url (or whatever name you use for your root) works in your controller
redirect_to root_url
Then, once redirect_to confirmed working, focus on getting the REST interface "request." information. There's a Rails 3 discussion here which may help you.
How to get request referer path?
I have an instance where I am recording prices for water from vendors. My vendor model has :price. However, I want to give users the option to input a price for different volumes, and do the simple division for them rather than having them to do it. In other words, users should be able to input $1.99 per liter or $3.99 for a gallon and so on. To do this, I need a virtual attribute in my form for :unit, since I don't want to be storing units in the table. Everything works well, except that I cannot seem to update vendor_params[:price] before I update the record or create a new record. This seems like it should be a cake walk, but I Googled most of the day and can't figure out how to make it work.
Here is what I have:
Model:
class Vendor < ActiveRecord::Base
attr_accessor :unit
...
end
Form:
<%= form_for(#vendor) do |f| %>
...
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
<%= select( "unit", "id", { "1 Liter" => "1", "Bottle (2 liters)" => "2", "Jerry Can (20 liters)" => "20"}) %>
</div>
...
<% end %>
Controller:
...
def update
vendor_params[:price] = vendor_params[:price].to_f/params[:unit][:id].to_f
respond_to do |format|
if #vendor.update(vendor_params)
format.html { redirect_to #vendor, notice: 'Vendor was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #vendor.errors, status: :unprocessable_entity }
end
end
...
end
I know that vendor_params[:price].to_f/params[:unit][:id].to_f returns the correct value. I just can't seem to assign that value to vendor_params[:price] before I update the record. I also tried the following which throws an error:
#vendor_params[:price] = vendor_params[:price].to_f/params[:unit][:id].to_f
It seems like this should be trivial! I guess I could use form_tag instead of form_for, but that seems odd when updating the full record. (The edit form has all fields for all the object attributes.) Anywho, I'm open to ideas and suggestions.
Thanks!!
If vendor_params is a strong_params method (which I'm assuming it is), it actually creates a new hash. So when you alter vendor_params... you're not actually changing your original params hash.
OK, why isn't vendor_params changing though... I dont care about params? WELL, vendor_params still points the original params hash assuming it looks something like:
def vendor_params
params.require(:vendor).permit(:price)
end
I think the link below is a similar issue and may present a useful solution. Hope I understood your problem correctly!
Modify ruby hash in place( rails strong params)
Let's say I create a scaffold:
rails g scaffold Cat name:string age:integer
and I add a presence validation on the Cat model's age attribute:
validates :age, presence: true
When I attempt to create a cat via the form, and put in the cat's name but purposely leave out the cat's age the controller bounces me back to the form but that cat's name is still present in the name field!
How is this happening?
I would have thought the
#cat = Cat.new
would replace all of the invalid cat's attributes. Maybe if it were #cat ||= Cat.new I could understand that more.
Also, how can I make this behaviour happen in a more complex rails app? I have a simple forum where topics has_many replies. I create my new replies via a form in my topic show view:
topic#show:
#reply = Reply.new
topic/show.html.erb:
<%= form_for [#toplic, #reply] do |f| %>
<%= f.text_field :name placeholder: 'Create a new name...' %><br>
<%= f.text_area :description, placeholder: 'Create a new description...', rows: 5 %><br>
<%= f.submit 'Create Discussion' %>
<% end %>
While everything works perfectly, when I purposely leave out a reply's name, though I am redirected back to the form and an error flash shows, my form is completely empty. All of the attributes have vanished? Why is this?
The key to understanding how this works is to realize that in the case of a form failure, the controller action is not rerun, but rather the template is rendered using the existing state from the action.
In a typical Rails scaffold, your create action will look like this
def create
#cat = Cat.new(cat_params) # instance variable is initialized with the form values
if #cat.save
redirect_to #cat, notice: 'Success!'
else
# in the case of form failure, we will re-render the 'new' template
# this will NOT rerun the entire 'new' action, thus the #cat variable
# will still maintain the values from the form that we gave it above
render 'new'
# note the difference if we had instead done a redirect_to; this would
# cause the CatsController#new action to be re-run which would reinitialize
# the #cat variable according to the code within the 'new' action
# redirect_to new_cat_url
end
end
For your more complex example, you'll want to follow the same procedure, making sure you just re-render the form and don't redirect to another action (which will cause the state to be lost).
# TopicsController
def show
#topic = Topic.find(params[:id])
#reply = Reply.new
end
# RepliesController
def create
#reply = Reply.new(reply_params) # init the var with the form values
if #reply.save
redirect_to #topic, notice: 'Success!'
else
# this is the key - we need to re-render the template of the previous action
# in this case, it would be the TopicsController#show template
render 'topics/show'
# Remember - if we instead do a redirect_to #topic, then we will lose the form
# values which are currently set in the #reply variable.
end
end
In short, make sure you recognize when you are redirecting to a new action versus just re-rendering a template.
One important GOTCHA to be aware of when re-rendering a template is that you must make sure that all the instance variables which exist for the controller action are available when you render the template.
For example,
# TopicsController
def show
#topic = Topic.find(params[:id])
#reply = Reply.new
#foo = Foo.new
end
# RepliesController
before_action :set_topic
def create
#reply = Reply.new(reply_params)
if #reply.save
# ...
else
# we need to remember to set up a #foo variable here otherwise it will be undefined
# when used within the 'show' template
#foo = Foo.new
render 'topics/show'
end
protected
def set_topic
#topic = Topic.find(params[:topic_id])
end
OK, so, you go go /cat/new. Rails' route for this URL runs the method CatsController#new, which renders the new.html.erb template. You put in your data, then hit submit. The action for this form is to POST to /cats, which runs the CatsController#create method. This method does this following:
#cat = Cat.new(cat_params)
It then tries to save the Cat. If it succeeds, it redirects you to the Cat's URL. If not, it re-renders the new.html.erb template. That's where the name comes from — the CatsController#update method creates its Cat from the values you put into the original form.
For a typical scaffold create action:
# POST /products
# POST /products.json
def create
#product = Product.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render json: #product, status: :created, location: #product }
else
format.html { render action: "new" } #will re-submit
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
The magic happens in the render method! which will submit the previous POST request (won't go back to the new action while a redirect will do), this way the submited values are still there. (check this SO question for more details)
That's said, this behavior relies on following the convention, however sometimes you need to give it a hand specially with some inputs (e.g selects, checkboxes, radio) might require extra setup using selected or value options
All, I have two dropdown boxes, which are populated from two different database tables and a form with a single submit button. My goal is to concatenate the two values upon form submit and write the single value back to the database into the form associated with the model.
More simply: two dropboxes allowing to select ['red','green','blue'] and ['dog','cat']. The user selects 'red' and 'cat', and the submit button creates a new record 'red-cat' (under the blogname model) as a result.
ENTIRE Form (new.html.erb) code:
<%= select("subdomainw1", "blognamew1", Subdomainw1.order("blognamew1 ASC").collect {|p| [ p.blognamew1 ] }, {:prompt => 'Select Adjective'}) %>
<%= select("subdomainw2", "blognamew2", Subdomainw2.order("blognamew2 ASC").collect {|p| [ p.blognamew2 ] }, {:prompt => 'Select Noun'}) %>
<%= simple_form_for (#blogname) do |f| %>
<%= f.button :submit %>
<% end %>
with the associated controller def create being:
def create
#blogname = Blogname.new(params[:blogname])
respond_to do |format|
#blogname.blogname = ?? THIS SHOULD BE A CONCATENATION OF THE VALUES FROM ABOVE SELECTS
if #blogname.save
format.html { redirect_to #blogname, notice: 'Blog was successfully created.' }
else
format.html { render action: "new" }
end
end
end
Any ideas here?
There are a lot of ways to do this, the Rails way would be to do it in your model and keep your controllers skinny.
I think the most common way in rails you'll see this done is a callback. So, for this example you could set up a before_validation (or perhaps before_create if you don't want it to be changed if the blog is edited) call back in your mode, and assign your blogname from the two other attributes.
model.rb
before_validation :generate_blogname
def generate_blogname
self.blogname ||= "#{blognamew1}-#{blognamew2}".parameterize
end
Then in your controller:
controller.rb
def create
#blogname = Blogname.new(params[:blogname])
respond_to do |format|
if #blogname.save
format.html { redirect_to #blogname, notice: 'Blog was successfully created.' }
else
format.html { render action: "new" }
end
end
end
The parameterize method will make this work for subdomains by taking out special characters. The model shouldn't probably be called blogname, it should probably be a table blog with an attribute of name. So #blog = Blog.new, then #blog.name = "Two Subdomain Values"
I have a single form of user attributes with about 60 fields, displayed separately by means of toggling hidden divs all on one page. When updating someone else' profile, all fields update as expected. When updating the current logged in profile (current_user), only about 40 of the fields update. Here's what I am observing on the update method for the current_user profile:
When setting a breakpoint directly after #user = User.find(params[:id]) and looking at the parameters that got passed, only about 40 out of the 60 form field parameters are even present. The ones that are present update as expected, and obviously the ones that aren't present don't update.
Any clues as to what might be causing this strange behavior?
Example: one of many mis-behaving form fields on users/_form.erb
<%= f.text_field :street_address, :placeholder => 'address..' %>
Update Method in users_controller.rb
# UPDATE
def update
#user = User.find(params[:id])
breakpoint_set = on_this_line
respond_to do |format|
if #user.update_attributes params[:user]
format.html do
redirect_to("/users", :notice => 'User Profile was successfully updated.')
format.xml { head :ok }
end
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
If the "currently logged in user" is just a User object, then I suspect you are seeing the side effects of caching.
If you have two Active Record objects that represent the same record, and they disagree about what the state of that record should be, different kinds of problems can happen. For example;
#u1 = User.find logged_in_user_id
#u2 = User.find logged_in_user_id
#u1.update_attributes :username => "root"
#u2.username # old username
#u2.reload
#u2.username # "root"
Make sure this isn't happening to you.
Check if the parameters are porting to the controller from client side. You may check this will Firefox + Firebug console.
And check if every parameter is under the user hash, because the missing parameters might not coming in the same user hash (which the Rails controller is looking at).