Json post hasMany to Rails 4 fails with Unpermitted parameters - ruby-on-rails

I'm trying to post a json message to a Rails 4.1.1 server, but is failing due to unpermitted parameters. I'm using Mongoid as well and submitting via POST and content type of application/json.
Here's my domain:
class Sale
include Mongoid::Document
include Mongoid::Timestamps
field :internalId, type: String
embeds_many :saleItems
accepts_nested_attributes_for :saleItems
end
Here's the controller code:
def sale_params
params.require(:sale).permit(:internalId, :parentInternalId, :externalId, :internalIdForStore, :internalIdForCustomer, :sendReceiptType, :saleItems)
end
# POST /sales
# POST /sales.json
def create
#sale = Sale.new(sale_params)
#####################
puts "parent: "
puts #sale.inspect
puts "collections: "
#sale.saleItems.each do |si|
puts "collection here"
puts si.inspect
end
respond_to do |format|
if #sale.save
format.html { redirect_to #sale, notice: 'Sale was successfully created.' }
format.json { render action: 'show', status: :created, location: #sale }
else
format.html { render action: 'new' }
format.json { render json: #sale.errors, status: :unprocessable_entity }
end
end
end
I've successfully saved the collection saleItems fine outside of rails and just using a ruby script with the collection successfully saving via Mongoid.
Here's the JSON content:
{
"sale" : {
"internalId":"77E26804-03CC-4CA9-9184-181C2D8CB02A"
"saleItems" : [
{
"inventoryName" : "inv 1"
},
{
"inventoryName" : "inv 2"
}
]
}
}

Wow I figured it out. It needs to have the {} around the collection of items.
params.require(:sale).permit(:internalId, :parentInternalId, :externalId, :internalIdForStore, :internalIdForCustomer, :sendReceiptType,
{:saleItems => [:inventoryName, :internalIdForSeller]})
Here's the post I found to help fix the issue.
Rails 4 - Strong Parameters - Nested Objects

I think the issue is the strong parameters being permitted.
You have
params.require(:sale).permit(:internalId, :parentInternalId, :externalId, :internalIdForStore, :internalIdForCustomer, :sendReceiptType, :saleItems)
But salesItems is another class. You need something like
params.require(:sale).permit(:internalId, :parentInternalId, :externalId, :internalIdForStore, :internalIdForCustomer, :sendReceiptType, :saleItems_attributes => [:inventoryName, :anotherAttribute, :stillAnotherAttribute])

Kindly edit your answer the tell that what params you are getting in.
The things is params is data structure its a request object. And permit is a method which allow to permit the specific parameter .
So put the debugger and easily you will recognize what the problem is.

Related

How can you check if a Rails rich_text_field (Action Text) is blank?

I can't seem to find this anywhere - the console shows the field as nil but in reality action text is storing the content which could be 'blank'.
MyModel.rich_text_field.nil? returns false regardless if the actual content is blank or not.
You can check if your model field is blank with:
MyModel.rich_text_field.blank?
This is how I ended up handling validations for Action Text fields to determine if they were empty.
in my posts_controller I made sure to have if #post.save within the respond_to block.
# POST /posts or /posts.json
def create
#post = current_user.posts.new(post_params)
respond_to do |format|
if #post.save
flash[:success] = "Post was successfully created."
format.html { redirect_to #post }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
in my Post model I added an attribute accessor with custom validations.
class Post < ApplicationRecord
attr_accessor :body
# Action Text, this attribute doesn't actually exist in the Post model
# it exists in the action_text_rich_texts table
has_rich_text :body
# custom validation (Note the singular validate, not the pluralized validations)
validate :post_body_cant_be_empty
# custom model validation to ensure the post body that Action Text uses is not empty
def post_body_cant_be_empty
if self.body.blank?
self.errors.add(:body, "can't be empty")
end
end
end
now the custom validation will be run to check if the Action Text post body is empty and if it is an error will be displayed to the user when the form is submitted.

nested form rails 4 save existing record on create

I am struggling to get this working. I have three models
Student
Classroomattnd
Classroom
Using the has_many :through relationship. All my relationships are defined correctly and I have setup the nested form using the accepts_nested_attributes.
So when creating a new student I want to select from a list of classrooms instead of creating a new classroom. The form part also works fine the part I am not getting is when I create the student it complains about the following error.
Couldn't find Classrooom with ID=3 for Student with ID=
I have searched around for few days now but can not get the answer I need to get this working.
def new
#student = Student.new
#student.classrooms.build
end
def edit
end
def create
#student = Student.new(student_params)
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice: 'Student was successfully created.' }
format.json { render :show, status: :created, location: #student }
else
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
Can someone help here, someone must of face this issue before?
Also in the rails console when I run the following it works:
classroom = Classroom.last
student = Student.create(name: 'Dave', classrooms:[classroom])
Your parameter handling isn't supporting nesting. You can look at request parameters in your server log or inspect the fieldnames of your generated form to be sure of your target. It's going to be something along the lines of
def student_params
params.require(:student).permit(:student => [:name, :classroom => [:id, :name]])
end
Or maybe as below. In this second case I'm not assuming everything in the form is nested under a student container. Also note the switch from classroom to classroom_attributes which is a change I have sometimes needed to make even though the form above is what the docs indicate.
def student_params
params.require(:name).permit(:classroom_attributes => [:id, :name])
end
Hopefully that gives you a notion of how to tailor your parameter definition to what your form is generating. Also note your error messages give you indication of what part of your definition is failing, eg the missing Student id in the error you quote.

Rails redirect if validation fails

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?

Ruby on Rails: is using params.require() important?

I'm trying to create a RESTful API for a very simple web app I made using Ruby on Rails. Specifically, I'm trying to implement the POST /users.json to create a new user.
The "parsing the response to JSON" bit is handled by the scaffolding. The issue comes when trying to use the strong parameters method scaffolded.
I make a POST request using the Postman Chrome extension to:
# POST /users
# POST /users.json
def create
user_params[:karma] = 1
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
So user_params is called, and it requires a user (note that this method was generated by the scaffolding):
def user_params
params.require(:user).permit(:name, :karma, :about)
end
I realised I can work this around by simply "not requiring" the user in params:
def user_params
params.permit(:name, :karma, :about)
end
But is this safe or appropriate? Is there a more correct way?
And why is the user even required in the first place, if that's exactly what I intend to create?
params.require(:user).permit(:name, :karma, :about)
Says that the params hash MUST contain a key called user and checks that associated value only contains the named keys. Aside from this security check, it returns pretty much what params[:user]. This requires that the params hash is of the form
{
:user => {
:name => "Bob",
:about => "Professional builder",
:karma => "10"
}
}
You'll get a parameters hash like this if the field names in the form / http request are user[name], user[about], which is exactly what you'll get if you use the rails form helpers
On the other hand it sounds like the parameters hash your sending is
{
:name => "Bob",
:about => "Professional builder",
:karma => "10"
}
because in your request the field names are name, about, karma.
The problem with doing params.permit(:name, :karma, :about) is that it stops you from ever passing other parameters to the action because the parameter checker won't allow them (and if you did allow them then User.new would complain).

How do I call update action from another action in rails 3?

So I'm writing a basic member modifying action, and I figured, lets stay DRY and just modify the params hash then pass along to our update method but it doesn't seem to work. I guess there is some rails magic going on that I can't find... From what I've read this should work. I'm using Rails 3.2.
Here's an example of what I'm trying to do:
# POST /tasks/1/toggle_done
def toggle_done
#task = Task.find(params[:id])
puts "<<<<<", params
# invert done bool value
params[:done] = !(#task.done)
# thought maybe update_attributes retured a full set of
# attributes in the params...
#params[:name] = #task.name + "...test."
# thought maybe the method call to update was getting
# filtered or something. Doesn't seem to help.
#params[:_method] = "put"
# redirect to update with these new params
puts ">>>>>", params
# Why bother rewriting task.done = x; task.save;
# redirect_to show; etc when update already does that.
update
end
# PUT /tasks/1
# PUT /tasks/1.json
def update
#task = Task.find(params[:id])
puts "======", params
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
I get the following console output:
<<<<<
{"_method"=>"post", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1"}
>>>>>
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
======
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
So it seems like the params array is set right. It renders the regular show view with the flash message "Task was successfully updated.", so it seems like the whole method gets executed but non of the model properties are getting changed. I guess something inside update_attributes is failing. Can anyone shed some light on this for me?
Also is this a crazy thing to do? Should I be setting and saving inside my toggle_done method instead of chaining to update?
Rails saves the attributes for the task object in the hash params[:task]. So you in your toggle_done method you need to save the result in params[:task][:done] otherwise rails cannot associate the done attribute with the task.
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
update
end
But with calling the update method you make 3 database queries where only 2 are neccessary - And the first 2 are identically because you load the Task with the ID in the toggle_done method as well as in update.
To avoid this you can put the save and redirect part into a protected method and call it when you want to save it. Like this:
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
save_updated
end
def update
#task = Task.find(params[:id])
save_updated
end
protected
def save_updated
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
You're passing params[:task] to update_attributes, which doesn't exist. Try:
params[:task] = {:done => !(#task.done)}

Resources