Using strong params when assigning model attributes in controller - ruby-on-rails

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"

Related

Can I have two different input fields and one column in the database?

I want to have two input fields, but only one column in the database.
The first input is stored data in numbers and the other one is stored data in numbers divided by 24.
You can put data only in one field.
Is there any possible way to do this?
UPD:
Migration:
def change
add_column :employees, :hourly_payment , :integer
end
View:
employees/_form.html.erb
<%= simple_form_for #employee do |form| %>
<%= form.input :name %>
<%= form.input :hourly_payment %>
<%= form.input :monthly_payment %>
<%= form.button :submit, class: "btn btn-success" %>
<% end %>
Your model and database tables are the internals of your application and are not actually tied to the view by anything except how easy ActiveRecord makes it to use convention over configuration to link the two*.
In Rails style MVC the controller is responsible for passing user input to the model. Usually you would just do this with simple mass assignment:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user)
.permit(:email, :salary)
end
end
This is basically just passing a whitelisted hash of parameters straight to the model as is and it all gets passed to the setters that ActiveRecord magically created for you by reading the database schema.
But there is nothing stopping you from assigning attributes manually:
class UsersController < ApplicationController
def create
#user = User.new(user_params) do |user|
user.salary = calculated_salary
end
# ...
end
private
def user_params
params.require(:user)
.permit(:email)
end
def calculated_salary
if params[:user][:hourly_payment].present?
params[:user][:hourly_payment]
elsif params[:user][:monthly_payment].present?
params[:user][:monthly_payment].to_i / 168
else
0 # sorry no cookies for you
end
end
end
Or monkeying with the parameters object:
def user_params
params.require(:user)
.permit(:email)
.merge(salary: calculated_salary)
end
It is after all just a hash on steroids. The only thing that Rails will prevent you from is passing a parameters object that has not been whitelisted.
There is no stone tablet for what you can do in a controller. The only thing to bear in mind is that controllers are notoriously hard to test and fat controllers are a recipe for disaster.
If you're doing anything more complicated there are better solutions such as form objects, decorators or service objects.
You'll need to create a view for that. Here is an example of migration:
def up
sql = %(CREATE OR REPLACE VIEW my_view_models AS
SELECT m.*,m.int_field*12 as my_new_value from my_models m
)
self.connection.execute(sql)
end
def down
self.connection.execute('DROP VIEW IF EXISTS my_view_models')
end
Then, you can access your value with method my_new_value on your model. You'll need to change the name of the default table matching your model.
class MyModel
self.table_name = 'my_view_models'
end
And access it via
MyModel.first.my_new_value

undefined method `permit' for "titleofcoolstuff":String

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/

How to pass controller parameters in Ruby on Rails

When I write a message and when pressing the send option,
I want to store student_id, coach_id and message to the database. student_id and coach_id are being saved, but the message field is not being saved. It shows null in the database. How do I fix this?
Any help is appreciated.
Controller file:
class CourseQueriesController <ApplicationController
def index
#course_query = CourseQuery.new
end
def create
# #course_query = CourseQuery.new(course_query_params)
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:message]).first_or_create
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def set_course_query
#course_query = CourseQuery.find(params[:id])
end
# def course_query_params
# params[:course_query].permit(:message)
# end
end
model/course_query.rb:
class CourseQuery < ActiveRecord::Base
belongs_to :student
belongs_to :coach
end
view/course_query/index.html.erb:
<%= simple_form_for (#course_query) do |f| %>
<%= f.button :submit , "Send or press enter"%>
<%= f.input :message %>
<% end %>
database /course_queries:
It seems you didn't permit :course_query.
Try to permit your params the following way:
def course_query_params
params.require(:course_query).permit(:message)
end
But according to the 2nd way you pass params (params[:message]) I think you have a bit different params structure. So try another one:
def course_query_params
params.permit(:message)
end
When you look into the params generated in the log, you will see that the message inside the course_query hash, so params[:message] should be params[:course_query][:message]
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message]).first_or_create

Rails form_for populates model with Nil

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])

Rails: Method declared in Application controller, but undefined according to browser

... used in orderscontroller#new to create an array, which is used in a select method of a form field in views, which is supposed to add a new order
Hullo there,
So my error is this
Routing Error
undefined local variable or method `array_of_payment_types' for #
Now, I've got this select field for a form to submit a new order, but my browser won't load the page with the form:
<div class="field">
<%= f.label :pay_type %><br />
<%= f.select :payment_type_id, Order::PAYMENTS_TYPES,
:prompt => 'Select a payment method' %>
</div>
This is using an array that I am trying to create twice:
class Order < ActiveRecord::Base
...
belongs_to :payment_type
**PAYMENT_TYPES = array_of_payment_types**
validates :name, :address, :email
validates :pay_type, :inclusion => { :in => PAYMENT_TYPES }
...
**def array_of_payment_types
PaymentType.pluck(:pay_type_name)
end**
end
and here:
class OrdersController < ApplicationController
...
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
**#PAYMENT_TYPES = array_of_payment_types**
#hide_checkout_button = true
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
...
end
while the method to create the array, which "plucks" the entries from the :pay_type_name column Payment_Type table, is declared both in order.rb and the ApplicationController, here:
class ApplicationController < ActionController::Base
protect_from_forgery
private
...
def array_of_payment_types
PaymentType.pluck(:pay_type_name)
end
end
Now I am trying to use the same process as other pieces of my application, just replicating stuff I have already done. For example, in OrdersController#new I've got
#cart = current_cart
current_cart is a method declared in the ApplicationsController and it works. So why doesn't array_of_payment_types also work?
Thanks for your help in advance :-)
Further information...
What I am trying to do with this is to create a new order, using a form, and one of the fields in the form enters a "pay_type" (or payment type in English). I want to present the user with options which is a list "plucked" from the entries in the PaymentType table, column :pay_type_name (I may be repeating myself, but no harm). But the new Order is created after the Order#new action, which is where I have created the array. Where/how should I create the array?
def array_of_payment_types in your Order class defines an instance method and you are trying to use it as a class method.
I'd just define array_of_payment_types as a class method instead and call Order.array_of_payment_types instead of the constant ARRAY_OF_PAYMENT_TYPES in your view.
You can always cache it in the class method, there's no need to use a constant for this.
def self.array_of_payment_types
#array_of_payment_types ||= PaymentType.pluck(:pay_type_name)
end
But, consider leaving the responsibility for the payment type array in the PaymentType class. The Order class shouldn't be the point of contact to retrieve data which is clearly under the control of another class.

Resources