I am having trouble populating my model with my parameter passed from form_for as it assigns Nil to the id, my column (decrypted), and created/updated at fields.
Below is my view (new.html.erb):
<%= form_for #decrypt_text, url: { action: "create" } do |f| %>
<div class="field">
<%= f.label :decrypt_text %><br>
<%= f.text_area :decrypted %><br>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
and my controller:
class DecryptController < ApplicationController
def new
#decrypt_text = Dmessage.new
end
def create
#decrypt_text = Dmessage.new(params[:decrypted])
p #decrypt_text
if #decrypt_text.save
redirect_to '/decrypt/display'
else
redirect_to '/'
end
end
def display
#displayme = Dmessage.order("created_at").last
end
end
and just in case, my model:
class CreateDmessages < ActiveRecord::Migration
def change
create_table :dmessages do |t|
t.text :decrypted
t.timestamps null: false
end
end
end
I know it's assigning nil values because this:
p #decrypt_text
prints out:
#<Dmessage id: nil, decrypted: nil, created_at: nil, updated_at: nil>
I really am not sure what I am missing but I am fairly new to Rails. Any help is appreciated!
First of all, remove url: { action: "create" } from your form. It is unnecessary for the reason that Rails is smart enough to understand that if #decrypt_text doesn't exist, it will trigger the necessary create action.
However, what I really think is causing you trouble is that you are not making use of strong parameters in your application (#decrypt_text = Dmessage.new(params[:decrypted])). You are trying to initialize a params hash directly, which is really dangerous. As of Rails version 4.x it is not allowed to initialize params hashes directly in this way, you must use strong params, to prevent mass assignment.
These would be the changes that I would personally make in your controller file...
class DecryptController < ApplicationController
def new
#decrypt_text = Dmessage.new
end
def create
#decrypt_text = Dmessage.new(decrypt_params)
if #decrypt_text.save
redirect_to '/decrypt/display'
else
redirect_to '/'
end
end
def display
#displayme = Dmessage.order("created_at").last
end
private
def decrypt_params
params.require(:dmessage).permit(:decrypted)
end
end
if u are using rails 4.x then your have to make use of strong parameters as of Rails version 4.x it is not allowed to initialize params hashes directly.
Add following private method to your controller,
def decrypt_params
params.require(:dmessage).permit(:decrypted)
end
As you are using Rails 3, the problem is this line
#decrypt_text = Dmessage.new(params[:decrypted])
It should be
#decrypt_text = Dmessage.new(params[:dmessage])
Related
everyone! I am new to rails and working on Codecademy tutorials. But I wanted to see if I can run the same app on my mac using VS Code and got into some roadblocks. The application is basically to create a form that takes in messages and displays it (in the index view). I wanted to explore changing the names of controller and model to what I want and guess I messed up the internal routing. Following is the controller (messagec)
class MessagecController < ApplicationController
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
render 'new'
end
end
private
def message_params
params.require(:message).permit(:content)
end
end
THe following is the model (messagesmo1)
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
The following is the routes.rb file
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'message_mo1s#create'
The following is the code in create.html.erb file
<%= form_for(#messages2) do |f| %>
<div class = "field">
<%= f.label :message %><br>
<%= f.text_area :content %>
</div>
<div class = "actions">
<%= f.submit "Create" %>
</div>
<% end %>
I am able to see the message list and able to go to create new message page. But when I submit the form, I am getting the following Routing error:
uninitialized constant MessageMo1sController Did you mean? MessagecController MessagesMController
My first questions is:
1) What am I missing in the routes.rb file?
2) Is there any rule between naming the model similar to that of the controller?
I just replicated all of the above, I think there are many things to keep in mind.
Your model file must be of name messagesmo1.rb and in this model:
class MessagesMo1 < ApplicationRecord
end
Your controller file should be of name messagec_controller.rb and in it:
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
redirect_to '/messages/new'
end
end
private
def message_params
params.require(:messages_mo1).permit(:content)
end
In the above point, look at the message_params part, it must be :messages_mo1 and not :message
No changes required in _form.html.erb file
Your migration file must be of name timestamp__create_messages_mo1s.rb and it must have:
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
In your routes.rb file, change the last route:
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'messagec#create'
Make sure all your links are updated in index.html.erb, in show.html.erb and in new.html.erb -> Like links to show, delete, edit etc. Or if your just testing remove these links.
After making above changes, run rails db:drop db:create db:migrate as it will clean your DB of old migration.
That's it, now everything should work. The main problem is naming convention should be standard across all files. So it's better to use standard convention.
It finally worked. Following are the 2 changes:
1) Instead of <%= form_for(#messages2) do |f| %>, I used a URL parameter
<%= form_for(#messages2, url:'/messages/') do |f| %>
2)As #cdadityang mentioned, I updated the params to params.require(:messages_mo1).permit(:content)
without the URL being given explicitly, I think the rails is assuming '/message_mo1' are the path. So the URL is basically taking it to 'messagec#create'
Hi i'm running into an issue with my code where i'm not sure where to use strong params. In this case I have a document object being set with a mix of preset values and values coming from my form for example.
class DocumentsController < ApplicationController
def add_document
document_name = params[:document_name]
document_parent_id = params[:doc_parent_id]
#document = Document.new(name: document_name, parent_id: document_parent_id, document_owner_id: current_user_id, created_by: current_user.name)
#document.save
#do flash stuff here
end
So the form is only submitting the document name and document parent id through the params hash. Should these two values be whitelisted using strong params? And If so how can I use strong params to create the new document with the other values that aren't coming from my form.
Thanks.
1/ Yes it should be whitelisted.
def add_document
# stuff
#document = Document.new(document_params.merge(
document_owner_id: current_user_id,
created_by: current_user.name
))
# stuff
end
def document_params
params.require(:document).permit(:name, :parent_id)
end
2/ To submit not from form, you just need to submit nested attribute document inside params alongside with other params:
{ document: { name: '<Name>', parent_id: '<Id>' }, other_params: '...' }
class DocumentsController < ApplicationController
def add_document
#document = Document.new document_params.merge(document_owner_id: current_user_id, created_by: current_user.name)
#document.save
end
private
def document_params
params.permit(:document_name, :doc_parent_id)
end
end
Your code really could do with improving a lot.
Firstly, Rails 4+ convention is to have a "top level" param value of the model (in your case document):
params: {
document: {
document_name: "x",
doc_parent_id: "y"
}
}
This would allow you to call the strong params method properly:
def document_params
params.require(:document).permit(:document_name, :doc_parent_id)
end
The way to achieve this is to use form_for (which should be used in conjunction with a RESTful controller):
#app/views/documents/new.html.erb
<%= form_for #document do |f| %>
<%= f.text_field :document_name %>
<%= f.submit %>
<% end %>
#app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
def new
#document = Document.new
end
def create
#document = Document.new document_params
#document.save
end
end
--
Finally, you also need to make sure your model attribute names work well.
You're currently using document_name as an attribute name. If it were my application, I'd call it name, allowing you to call #document.name in the future.
The same for your other attributes:
document_name -> "name"
doc_parent_id -> "parent_id"
document_owner_id -> "owner_id"
I am just trying to get two parameters from a view to my controller. I'm using Rails 4.2.x and strong params are killing me.
One param, :query, resolves correctly. However the second param, :location, throws the error in the questions title. I have Googled the issue but everyone's scenario seems to be different and their solutions (relatively) unique.
The view in question is index.html.erb which only contains a simple search form.
<%= form_tag("/searches", action: "create", method: "post") do %>
<div>Job Title</div>
<%= text_field_tag(:query) %>
<div>Location</div>
<%= text_field_tag(:location) %>
<%= submit_tag("Go") %>
<% end %>
The controller in question is searches_controller.rb.
class SearchesController < ApplicationController
def index
binding.pry
end
def show
binding.pry
end
def update
end
def create
#query = search_params["query"].to_s || nil
#location = search_params[:location].to_s || nil
binding.pry
end
def delete
end
private
def search_params
params.require(:query).permit(:location)
end
end
The stack trace points to the search_params method, and shows me that I have the following params in the controller
{
"utf8"=>"✓",
"authenticity_token"=>"DEcTwT/NnSY3S3n25zZGXD+KRZcsRkWj9bmN57AMNivFbMXwHF5Vf/psgzSMkZPBa+OWJgafXYGdW+o5KN3xxg==",
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
}
What am I missing?
Strong parameters is for providing a hash of attributes, for example:
<%= form_for #user do |f| %>
## form
<% end %>
This may send parameters like this:
"user" => { "name"=> "Your Name", "age" => "23", "location" => "USA" }
Strong parameters in this case would be instructing rails to process the users hash of attributes and specifically these attributes, like this:
params.require(:user).permit(:name, :age, :location)
In your case, you are passing in individual parameters (not hashes of attributes), so if you want to grab them, you grab them explicitly:
def create
#query = params[:query].to_s || nil
#location = params[:location].to_s || nil
#do something
end
No need for strong parameters to whitelist model attributes here. Hope this helps.
In your case
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
since your data are not wrapped with any keys (they are at the root) so you can simply access them using like params[:query].
Whitelisting/Strong params
We need to whitelist params only for mass assignment. like #user.update(user_params) Here, unless the params sent by users in user_params are whitelisted i.e. permitted using .permit method; the update method will throw an exception ActiveModel::ForbiddenAttributes.
In your case since your not updating anything you do not need to create strong params for it.
def create
#query = params["query"].to_s || nil
#location = params[:location].to_s || nil
binding.pry
end
If you are gonna do mass assignment in future you have to whitelist your params
For more info see https://cbabhusal.wordpress.com/2015/10/02/rails-strong-params-whilisting-params-implementation-details/
I'm very new to Ruby on Rails and trying to create a search function that allows the user to serach multiple parameters at the same time; from, and to. Something to keep in mind is that there will probably be even more parameters later on in the development. I've got it to work when searching for one of the fields, but not more than that.
Search view:
<%= form_tag(journeys_path, :method => "get", from: "search-form") do %>
<%= text_field_tag :search_from, params[:search_from], placeholder: "Search from", :class => 'input' %>
<%= text_field_tag :search_to, params[:search_to], placeholder: "Search to", :class => 'input' %>
<%= submit_tag "Search", :class => 'submit' %>
<% end %>
Method:
class Journey < ActiveRecord::Base
def self.search(search_from)
self.where("from_place LIKE ?", "%#{search_from}%")
end
end
Controller:
class JourneysController < ApplicationController
def index
#journeys = Journey.all
if params[:search_from]
#journeys = Journey.search(params[:search_from])
else
#journeys = Journey.all.order('created_at DESC')
end
end
def search
#journeys = Journey.search(params[:search_from])
end
end
I've tried some gems and all kind of solutions that I've found in other questions, but I'm just not good enough at RoR yet to succesfully apply them correctly without help. I would appreciate any help I can get.
Thank you!
Model:
class Journey < ActiveRecord::Base
def self.search(search_from, search_to)
self.where("from_place LIKE ? and to_place LIKE ?", "%#{search_from}%", "%#{search_to}%")
end
end
Controller:
class JourneysController < ApplicationController
def index
if params[:search_from] and params[:search_to]
#journeys = search
else
#journeys = Journey.all.order('created_at DESC')
end
end
def search
#journeys = Journey.search(params[:search_from], params[:search_to])
end
end
The best approach here is to incapsulate your search form as a separate Ruby class. Using Virtus here helps to get type coercion for free.
class SearchForm
include Virtus.model # Our virtus module
include ActiveModel::Model # To get ActiveRecord-like behaviour for free.
attribute :from, String
attribute :to, String
# Just to check if any search param present,
# you could substitute this with validations and just call valid?
def present?
attributes.values.any?{|value| value.present?}
end
```
In Rails 3 IIRC you also have to include ActiveModel::Validations to be able to validate your form input if needed.
Now, let's see how to refactor controller. We instantiate form object from params and pass that to the model query method to fetch records needed. I also moved ordering out of if clause and used symbol ordering param - cleaner IMO.
def index
#search_form = SearchForm.new(search_params)
if #search_form.valid? && #search_form.present?
#journeys = Journey.search(#search_form)
else
#journeys = Journey.all
end
#journeys = #journeys.order(created_at: :desc)
end
def search
#journeys = Journey.search(SearchForm.new(search_params)
end
private
def search_params
params.require(:search_form).permit(:from, :to)
end
Now to the view: form_for will work perfectly with our form object, as will simple_form_for
<%= form_for #search_form, url: journeys_path, method: :get do |f| %>
<%= f.text_field :from, placeholder: "Search from", class: 'input' %>
<%= f.text_field :to, placeholder: "Search to", class: 'input' %>
<%= f.submit "Search", class: 'submit' %>
<% end %>
View looks now much shorter and cleaner. Incapsulating params in object makes working with search params muuuuch easier.
Model:
class Journey < ActiveRecord::Base
def self.search(search_form)
if search_form.kind_of?(SearchForm)
journeys = all # I'm calling Journey.all here to build ActiveRecord::Relation object
if search_form.from.present?
journeys = journeys.where("from_place LIKE ?", "%#{search_form.from}%")
end
if search_form.to.present?
journeys = journeys.where("to_place LIKE ?", "%#{search_form.to}%")
end
else
raise ArgumentError, 'You should pass SearchForm instance to Journey.search method'
end
end
end
Notice how I build ActiveRecord::Relation object by calling Journeys.all and applying each search param if present. Chaining where like that would put AND in between automatically, if you need OR Rails 4 has it: Journey.or(condition).
Pros of this approach:
You are using Plain Old Ruby Classes, almost no magic here, and it works like usual Rails model in many ways. Putting search params in the object makes it a lot easier to refactor code. Virtus is the only dependency, sans Rails itself of course, and it's more for convenience and to avoid writing boring boiler-plate code.
You can easily validate input if needed (If you really want to be strict about input and show user validation error instead of silently executing stupid query with contradicting conditions and returning no results).
I'm new to developing in Ruby on Rails and I'm stuck on a little project that I've been working on to understand RoR better. I am trying to make a little weather website and I'm having trouble sending user input to a model through a controller and to have that model use send back the correct information so that I can parse it and what not. I have not been able so far to send the user param along to the controller so that it will send out the right request. Here is my following code:
hourly.html.erb:
<%= form_tag('/show') do %>
<%= label_tag(:location, "Enter in your city name:") %>
<%= text_field_tag(:location) %>
<br />
<%= submit_tag "Check It Out!", class: 'btn btn-primary' %>
<% end %>
hourly_lookup_controller.rb:
class HourlyLookupController < ApplicationController
def show
#hourly_lookup = HourlyLookup.new(params[:location])
end
end
hourly_lookup.rb:
class HourlyLookup
def fetch_weather
HTTParty.get("http://api.wunderground.com/api/api-key/hourly/q/CA/#{:location}.xml")
end
def initialize
weather_hash = fetch_weather
assign_values(weather_hash)
end
def assign_values(weather_hash)
more code....
end
end
Any help or directions to some good examples or tutorials would be greatly appreciated. Thanks
If you want to send a variable to HourlyLookup, you'll need to do so:
class HourlyLookupController < ApplicationController
def show
#hourly_lookup = HourlyLookup.new(params[:location])
#hourly_lookup.fetch_weather
end
end
class HourlyLookup
attr_reader :location
def initialize(location)
#location = location
end
def fetch_weather
response = HTTParty.get("http://api.wunderground.com/api/cdb75d07a23ad227/hourly/q/CA/#{location}.xml")
parse_response(response)
end
def parse_response(response)
#parse the things
end
end