This question is about problems I ran into after asking my previous question Rails - How do I refresh a page without reloading it?
Have been googling but cannot find a solution.
I'm building a function that gives out a random record every time people go to that page but the random record will change again if the user refresh the page.
Therefore I have tried using cookies
#posts_controller.rb
def new
#random = cookies[:stuff] ||= Stuff.random
end
and call #random.content in my posts/new view since I want only the content of the Stuff model, when I go to the page for the first time is fine but when I refresh the page, it gives me this error
undefined method 'content' for "#<Stuff:0x0000010331ac00>":String
Is there a way to resolve this?
Thanks!
----Update----
I have fixed the content missing problem using <%= show_random(#random)['content'] %> and the Internal Server Error expected Hash (got Array) for param 'post'using
<%= f.hidden_field :stuff_id , :value => show_random(#random)[:id] %>
stuff/new.html.erb
# app/views/stuff/new.html.erb
<%= show_random(#random)[:content] %>
<div>
<%= f.hidden_field :stuff_id , :value => show_random(#random)[:id] %><br>
</div>
But when creating the Post without meeting the validation, it gives me
no implicit conversion of Stuff into String
Extracted source (around line #2):
1
2 <%= show_random(#random)['title'] %>
I think it has something to do with my create action in my Posts_controller.erb
Please have a look below of my Posts_controller.erb
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Post created successfully!"
else
#random = Stuff.where(id: params[:post][:stuff_id]).first
render 'new'
end
end
The first time , as cookies[:stuff] is null, so a new Stuff instance is assigned to both cookies[:stuff] and #random, so the content method call on #random will be fine. But as you store an object into the cookies[:stuff], the value will be converted into a string by rails automatically.
The second time, you visit the page, the cookies[:stuff] is not empty, and is assigned to the #random variable. But as previous saying, the content inside the cookies is a string, so calling content method on a string can not work.
Make your .random method defined on Stuff return a serialized record, which you can then deserialize in your views (possibly with a helper method) to make it a hash:
# app/models/stuff.rb
def self.random
random_record_magic.to_json
end
end
# app/views/stuff/index.html.erb
<%= show_random(#random)['content'] %>
# app/helpers/stuff_helper.rb
def show_random(random)
JSON.parse(random)
end
Related
I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.
the f.error_messages form builder helper has been depreciated in Rails 3.
How do I get the error messages for a form with the opening tag:
<%= form_for Model.new %>
(I'm using Model.new because I want to be able to load an undefined multiple number of these forms onto a single page)
You can't. Not with a form builder like this one.
Basically, error messages are stored inside the object you use to build a form. If you build a new one every time, you get a clean object without errors.
What you need to do is persist the object filled in by the user between requests. Typically this is done by creating a new object in controller:
#model = Model.new
The essence of this is, that new view uses #model to render a form. And the trick is to have a possibility to render the same view in other actions that also provide #model. That said, if you do something like this in create:
#model = Model.new(model_params)
if #model.save
# success
else
render :new
end
It can render new view, because it assigns #model too; in this case, it will contain errors with messages and other stuff. All this is inside #model.errors – which is always empty in new action.
It's not that different for multiple forms, bear in mind that you always submit only one. You may switch to rendering an array of forms, in that case you could have #models array:
#models = [form1, form2, form3]
In that case, if saving fails, assign that array again and either replace the form the user tried to fill in (if you can identify it), ot prepend/append that form with errors to that array.
#models[index_of_submitted_form] = form_from_user #replace
#models << form_from_user # append
#models.unshift form_from_user # prepend
In Rails 3, you can access errors using #model.errors
<%= form_for #model = Model.new do |f| %>
<% #model.errors.full_messages.each do |msg| %>
<p><%= msg %></p>
<% end %>
# ...
# ...
# ...
<% end %>
I am trying to upload a photo but after I press the upload button, I get this error. I am new to rails 4 so I'm not sure what I am missing.
My logic is when I click the submit button. This will cause the create action to fire and create a IncomePicture object and store it in my database.
No route matches [POST] "/income_pictures/new"
Routes:
root_path GET / static_pages#home
income_pictures_path GET /income_pictures(.:format) income_pictures#index
POST /income_pictures(.:format) income_pictures#create
new_income_picture_path GET /income_pictures/new(.:format) income_pictures#new
edit_income_picture_path GET /income_pictures/:id/edit(.:format) income_pictures#edit
income_picture_path GET /income_pictures/:id(.:format) income_pictures#show
PATCH /income_pictures/:id(.:format) income_pictures#update
PUT /income_pictures/:id(.:format) income_pictures#update
DELETE /income_pictures/:id(.:format) income_pictures#destroy
Controller:
class IncomePicturesController < ApplicationController
def new
#income_picture = IncomePicture.new
end
def create
#income_picture = IncomePicture.new(IncomePicture_params)
if #income_picture.save
flash[:notice] = "Income picture successfully uploaded"
redirect_to #income_picture
end
end
def show
#income_picture = IncomePicture.find(params[:id])
end
def index
#income_picture = IncomePicture.all
end
private
def IncomePicture_params
params.require(:income_picture).permit(:image, :name)
end
end
View:
<%= form_for :income_picture, :html => { :multipart => true } do |f| %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :image %>
<%= f.file_field :image %>
</p>
<p><%= f.submit %></p>
<% end %>
I think you want form_for #income_picture rather than form_for :income_picture.
From the form guide: Using a symbol creates a form to new_income_picture_path, i.e. /income_picture/new whereas using a populated instance variable creates a form to income_pictures_path, i.e. income/pictures. Both set the form's method to POST. However, there's no such route as POSTing to /income_picture/new/, which is what caused the error.
form_for
To elaborate on the accepted answer, you have to remember that when calling form_for, Rails does some pretty amazing things:
It takes an ActiveRecord object and builds a "route" out of it (from the model)
It populates the form with the ActiveRecord object's data
It allows you to retain a perceived persistent state on the form (by perpetuating the data)
The problem you have is you're passing a simple symbol to the form - which prevents Rails from being able to accurately access the data required to make the 3 "magic" steps above possible.
This means you'll get random errors like the one you're seeing (IE in the absence of an ActiveRecord object, Rails will just use the same URL that you have on your page - /new)
--
ActiveRecord
The way to fix the issue you have is to replace the symbol with an ActiveRecord object, which was suggested in the accepted answer.
The reason why using an ActiveRecord object (#instance_variable) works is because of Ruby's core functionality -- it's a object orientated language. Being object orientated, it means that each time you populate an ActiveRecord object, you'll basically give Rails a series of other information, such as model_name etc.
This means when you pass the #instance_variable to the form_for method, Rails will be able to take the data from ActiveRecord & process it on screen for you
I'm having problems implementing a kind of comments form, where comments (called "microposts") belong_to both users and posts, users have_many comments, and posts (called "propositions") have_many comments.
My code for the comments form is:
<%= form_for #micropost do |f| %>
<div class="field">
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
The MicropostsController has this in the create action:
def create
#proposition = Proposition.find(params[:proposition_id])
#micropost = current_user.microposts.build(params[:micropost])
#micropost.proposition = #proposition
if #micropost.save
flash[:success] = "Contribution submitted"
redirect_to root_path
else
#feed_items = []
render 'pages/home'
end
end
The form for creating a new micropost is on the same page as a proposition, yet the proposition id doesn't seem to get passed at any point.
This is the error I get on submitting the micropost form:
ActiveRecord::RecordNotFound in
MicropostsController#create
Couldn't find Proposition without an
ID
Parameters are:
{"commit"=>"Submit",
"micropost"=>{"proposition_id"=>"",
"content"=>"First comment"},
"authenticity_token"=>"TD6kZaHv3CPWM7xLzibEbaLJHI0Uw43H+pq88HLZFjc=",
"utf8"=>"✓"}
I'm completely new to rails and very new to coding anything at all, so I'd be grateful for any help you can give me!
Thanks in advance.
Your params are:
"micropost"=>{"proposition_id"=>"", "content"=>"First comment"}
So to get proposition_id, you have to do :
params[:micropost][:proposition_id]
But this is empty. And there is nowhere else to get this id, that's why this line retrieves nil:
#proposition = Proposition.find(params[:proposition_id])
Making this fail:
#micropost.proposition = #proposition
You must either:
add the proposition_id as an hidden_field
store it in session
But I don't know your context enough to give you the proper solution here.
EDIT:
In your link, replace:
<%= f.hidden_field :proposition_id %>
with:
<%= f.hidden_field :proposition_id, :value => #proposition.id %>
If it doesn't work, show your params.
Note: it's bad practice to rely on instance variables, you should send local variable to each partial
As you can see, the proposition_id parameter is empty, so your controller can't find the proposition unless you give it a valid id.
You need to make sure your new form sends the proposition_id attribute. One way to do this is:
Set the proposition in the new action in the controller: #micropost.proposition = ...
In the form, add a hidden field for the id: f.hidden_field :proposition_id
In the create action, find the appropriate Proposition with params[:micropost][:proposition_id]
(You'll also want to make sure to use attr_accessible in your Micropost model, and make sure proposition_id is NOT in that list. Otherwise, you'll be open to nasty security holes. See http://www.kalzumeus.com/2010/09/22/security-lessons-learned-from-the-diaspora-launch/ and Which fields should be protected from mass assignment?)
EDIT (due to comment):
Your new action should be like this:
def new
#micropost = Micropost.new
#micropost.proposition_id = params[:proposition_id]
This is slightly different from what is said above, and is due to the fact you're sending the proposition id in the request to new. There's no need to look up the actual proposition record, since we're only interested in the id field (which we already have).
I have the following model:
class Activity < ActiveRecord::Base
has_many :clientships, :dependent => :destroy, :after_add => :default_client_info
accepts_nested_attributes_for :clientships, :allow_destroy => true
end
In my controller, if I perform the following
def new
#activity = IndividualActivity.new(params[:activity])
#activity.clientships.build(:client => Client.first)
...
end
and then save the form, it creates the relevant params and submits successfully.
However, if I chose to call the following through a remote link
#activity.clientships.build(:client => Client.last)
the view is updated with the new clientship record but when I submit the form, the params[:activity] is not created for the second nested attribute. (Why not!?)
This is the view:
%h1 Create a new Activity
- form_for #activity do |f|
%div
= render "activities/client_selector", :f => f
%div
= f.submit "Save!"
Here is the remote_link's controller action
def add_client
#activity = IndividualActivity.new(session[:individual_activity])
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
respond_to do |format|
format.js
end
end
This is the add_client.html.js:
page.replace_html "selected_clients", :partial => 'activities/clients'
This is the activities/clients partial:
- form_for #activity do |f|
- f.fields_for :clientships do |client_f|
%tr
%td= client_f.hidden_field :client_id
%td= client_f.object.client.full_name
Does anyone know how I can troubleshoot this further? I seem to have come to a dead-end with my debugging... One thing to note, there is a double use of the following form_for used in new.html.haml and the activities/clients partial (is this problematic?)
- form_for #activity do |f|
I am on rails v2.3.5
Thanks
You ask about debugging, so the first step may be looking at the server log (log/development.log).
There you should see the "params" hash.
Maybe your params contain "activity"=>{"client_id"=>..} instead of "client_id"=>.. ?
Also look at the generated HTML page - use a Firebug or just use a "view source" method of your browser. Look, especially, for input names.
If everything looks OK, put a few debug calls in your action, and look at the development.log for some database activity - do the SQL queries look like they are doing what you want?
In your question there is no 'save' method. The 'build' method does NOT save the created record. Maybe this is your problem?
def add_client
logger.debug "Creating Activity"
#activity = IndividualActivity.new(session[:individual_activity])
logger.debug "Building clientship"
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
logger.debug "#activity = #{#activity.inspect}"
# Maybe you were missing this part of code?
logger.debug "Saving #activity"
#activity.save! # use a ! to easily see any problems with saving.
# Remove in production and add a proper if
logger.debug "Saved. #activity = #{#activity.inspect}"
respond_to do |format|
format.js
end
end
You should create a functional test (in case you haven't already) and ensure that if you send proper parameters, your action works as intended.
The test will narrow your search. If the test fails, you know you have a problem in the action. If the test is OK, you need to ensure the parameters are sent properly, and you probably have the problem in your view.
UPDATE:
You said you have TWO forms on the page. This may be the problem, since only one form may be sent at a time. Otherwise it would need to work in a way which can send two requests in one request.
First thing (useful in all similar problems): validate whether your page has correct HTML structure - for example http://validator.w3.org would be a good start. Try to make the code validate. I know that some people treat a "green" status as a unachievable mastery, but just it's really not so hard. With valid code you may be sure that the browser really understands what you mean.
Second: Place all your inputs in a single form. You have problems with nested attributes. For start, try to manually insert inputs with name like <input name="activity[clientship_attributes][0][name]" value="John"/>, and for existing clientships ensure that there is an input with name = activity[clientship_attributes][0][id].
This is the way nested attributes are handled.
Your view may create such fields automagically. This construction should be what you need: (it worked in one of my old project in rails 2.x, I have just replaced the names with ones you use)
<% form_for(#activity) do |f| %>
<p><%= f.text_field :activity_something %></p>
<% #activity.clientships.each do |clientship| %>
<% f.fields_for :clientships, clientship do |cform| %>
<p><%= cform.text_field :name %></p>
<p><%= cform.text_fiels :something %></p>
<% end %>
<% end %>
<% end %>
If you really want to use a partial there, don't create a new form in the partial. Use only the parts of above code.
To pass a variable to the partial, use :locals attribute in the place where you call render :partial:
<%= render :partial => 'clientship', :locals => {:form => f} %>
Then, in your partial, you may use a local variable form where you would use f outside of the partial. You may, of course, map the variables to the same name: :locals => {:f => f}