I'm trying to work out how to use a post api I have created. I'm using the form-data input type in the PostMan chrome plugin and it can get data created into the DB but when I try to structure the raw API the post is successful but it doesn't identify the values (and therefore inserts a blank record into the DB).
My controller code:
def create
#newProduct = Product.new
if #newProduct.create(product_params)
render json: {error: "Product created"}
else
render json: {error: "Product failed to create"}
end
end
def product_params
params.require(:product).permit(:name, :brand)
end
I have tried the following
{"brand"=>"wut",
"name"=>"asdasd"}
[
{
"name":"name1",
"brand" : "brand"
}
]
EDIT:
Using:
{ "product"=>{"name"=>"some name", "brand"=>"some brand"} }
and the following controller:
def create
#newProduct = Product.create(params[:product])
if #newProduct.save
render json: {message: "Product created"}
else
render json: {error: "Product failed to create"}
end
end
private
def product_params
params.require(:product).permit(:name, :brand)
end
I still get empty records being entered
Any ideas?
Thanks
def create
#newProduct = Product.new(product_params) #do .new here .create does new and save in 1 step
if #newProduct.save
render json: {message: "Product created"}
else
render json: {error: "Product failed to create"}
end
end
private
def product_params
params.require(:product).permit(:name, :brand)
end
If you're formatting the raw post data you may need to do something like this product%5Bname%5D=test+name&product%5Bbrand%5D=test+brand but that is probably not the case as you aren't getting a 400 error, this is the escaped version of this product[name]=test name&product[brand]=test brand
Related
I need to update the values of many attributes of a table in rails . I am using inline-edit for that. Please see the below code and give me suggestions to improve the code in a better way(modularization,meta-programming...).
products_controller.rb
def update
#page=params[:page] # In normal update no such param, so initialized with nil. If inline edit, that will be "inline"
#page=="inline"? inline_update: update_by_app_params
end
#Method to update by inline Update of individual params
def inline_update
if inline_edit_param_contains_description?
status = Product.update_description(params,current_user.id,#product)
elsif inline_edit_param_contains_order_number?
status = Product.update_order_number(params,current_user.id,#product)
elsif inline_edit_param_contains_date?
status = Product.update_end_date(params,current_user.id,#product)
..........................
..........................
end
(status==true)? respond_block : rescue_block
end
def respond_block
respond_to do |format|
format.json { render json: {status: true, description: #product.description} } if inline_edit_param_contains_description?
format.json { render json: {status: true, order_number: #product.order_number} } if inline_edit_param_contains_order_number?
...........................
...........................
end
def rescue_block
flash.now[:error] = #product.errors.full_messages.uniq.join(', ')
respond_to do |format|
format.json { respond_with_bip(#product) }
end
end
#Method to check description in inline edit
def inline_edit_param_contains_description?
params[:product][:description]
end
#Method to check order number in inline edit
def inline_edit_param_contains_order_number?
params[:product][:order_number]
end
..............
..............
product.rb
#This method will update the description of product
def self.update_description params,user_id,product
product.order=product.request_no
status = product.update(:description=>params[:product][:description],:last_modified_by=>user_id)
ProductHistory.update_history product, 'updated' if(status==true)
status
end
#This method will update the order_number of product
def self.update_order_number params,user_id,product
product.order=product.request_no
status = product.update(:order_number=>params[:product][:order_number],:last_modified_by=>user_id)
ProductHistory.update_history product, 'updated' if(status==true)
status
end
.................
.................
Please help to improve the code.
Thanks in advance
I think you can just do: #product.update(product_params) according to the standard CRUD operation. Rails is smart enough to understand that just one attribute has been changed and will adjust its UPDATE statement accordingly.
You can check this in the server log if you want to.
Then, my suggestion woud be to pass back the changed fields by using the changed_attributes option and handle this entire JSON object client side (since it only contains the changed values you can process them all):
class ProductController < ApplicationController
...
def update
product.update(product_params)
respond_to do |format|
format.json { render json: product.changed_attributes }
end
end
private
def product
#product ||= current_user.products.find(params[:id])
end
def product_params
params.require(:product).permit(:name, :description, :order_number, ...)
end
end
I am building a Rails API and found out that put request passes without required parameters. That is weird for me as app won't allow post request without parameters. Moreover, when I’m trying to update the spending without attributes via Rails console, it fails. But via Postman/CURL request passes successfully
The controller looks like this:
class SpendingsController < ApplicationController
before_action :find_spending, only: %i[show update destroy]
def create
spending = Spending.new(spending_params)
spending.user = current_user
spending.category = Category.find_by(id: spending_params[:category_id])
if spending.valid?
spending.save
render json: SpendingSerializer.new(spending), status: :ok
else
render json: ActiveRecordErrorsSerializer.new(spending), status: :bad_request
end
end
def index
spendings = Spending.where(user_id: current_user.id).order("#{sort_spendings}")
total_value = Spending.where(user_id: current_user.id).pluck(:amount).sum
render json: {spendings: SpendingSerializer.new(spendings), total_amount: total_value}, status: :ok
end
def show
if #spending.valid?
render json: SpendingSerializer.new(#spending), status: :ok
else
render json: ActiveRecordErrorsSerializer.new(#spending), status: :not_found
end
end
def update
if #spending.valid?
#spending.update(spending_params)
render json: SpendingSerializer.new(#spending), status: :ok
else
render json: ActiveRecordErrorsSerializer.new(#spending), status: :bad_request
end
end
def destroy
if #spending.destroy
head :no_content
else
render json: ActiveRecordErrorsSerializer.new(#spending), status: :not_found
end
end
private
def spending_params
params.require(:spending).permit(:description, :amount, :category_id)
end
def find_spending
begin
#spending = Spending.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: {errors: "Spending with id #{params[:id]} not found"}, status: :not_found
end
end
def sort_spendings
sort = { sort_by: "created_at", sort_dir: "desc"}
sort[:sort_by] = params[:sort_by].split(" ").first if params[:sort_by].present?
sort[:sort_dir] = params[:sort_by].split(" ").last if params[:sort_by].present?
sort.values.join(" ")
end
end
And my model:
class Spending < ApplicationRecord
belongs_to :user
belongs_to :category
validates :description,
presence: true
end
I’m really out of ideas, why is that happening. Any guesses what can that be related to?
First thing that I noticed is your update method. You check validation before updating the model. #spending.valid? always returns true in this case. My suggestion to modify it. #spending.update(spending_params) returns true if it update is successful and false if it fails.
def update
if #spending.update(spending_params)
render json: SpendingSerializer.new(#spending), status: :ok
else
render json: ActiveRecordErrorsSerializer.new(#spending), status: :bad_request
end
end
created method an be also optimised. You don't need find and assign category separately. It will be assigned as all spending_params.
def create
spending = Spending.new(spending_params)
spending.user = current_user
spending.save
render json: SpendingSerializer.new(spending), status: :ok
else
render json: ActiveRecordErrorsSerializer.new(spending), status: :bad_request
end
end
In my rails controller, I have to check after getting #group with before_action that this group is not system.
But I have lot's of repetition in my controller. I've tried to turn into a separate method but I get the classic :
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
Here is a part of my code without the separate method who give me the error.
def destroy
if #group.is_system?
render json: { errors: 'You can\'t delete a group system' }, status: 403
return
end
...
end
def update
if params[:group] && !params[:group].empty?
if #group.is_system?
render json: { errors: 'You can\'t edit a group system' }, status: 403
return
end
...
else
render json: { errors: 'Missing correct parameters' }, status: :unprocessable_entity
end
end
.....
You could have in a parent controller:
def render_errors(errors, status)
render json: { errors: Array(errors) }, status: status
end
def render_403(errors)
render_errors(errors, 403)
end
def render_422(errors)
render_errors(errors, 422)
end
then in your action:
before_action :check_system
def check_system
# I assume you already defined #group
render_403('You can\'t delete a group system') if #group.is_system?
end
Notice I changed a bit of your code: having errors key which is only a string is very misleading, should be an array.
This code is for a UserList (a user can create a User To-Do List). This particular resource does not hold the list items, but just the title of the list, and the type of list.
class Api::V1::UserListsController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
def index
if authenticate_user
user_lists = #current_user.user_lists
if user_lists
respond_with user_lists, each_serializer: Api::V1::UserListSerializer
else
render json: { error: "Could not find user's lists."}, status: :not_found
end
else
render json: { error: "User is not signed in." }, status: :unauthorized
end
end
def show
if authenticate_user
user_lists = #current_user.user_lists
user_list = user_lists.find_by_id(params[:id])
if user_list
respond_with user_list, serializer: Api::V1::UserListSerializer
else
render json: { error: "Could not find user's list."}, status: :not_found
end
else
render json: { error: "User is not signed in." }, status: :unauthorized
end
end
def create
if authenticate_user
user_list = #current_user.user_lists.new(user_list_params)
if (user_list.save!)
respond_with :api, :v1, #current_user, user_list, serializer: Api::V1::UserListSerializer
else
render json: { error: "Could not create new User List."}, status: :unprocessable_entity
end
else
render json: { error: "User is not signed in." }, status: :unauthorized
end
end
def update
if authenticate_user
user_list = #current_user.user_lists.find_by_id(params[:id])
if (user_list.update_attributes(user_list_update_params))
respond_with :api, :v1, #current_user, user_list, serializer: Api::V1::UserListSerializer
#respond_with user_list, serializer: Api::V1::UserListSerializer
else
render json: { error: "Could not update User List." }, status: :unprocessable_entity
end
end
end
private
def user_list_params
params.require(:user_list).permit(:user_id, :type_id, :title)
end
def user_list_update_params
params.require(:user_list).permit(:type_id, :title)
end
end
Now the update works when I PUT/PATCH... but I get a
Completed 204 No Content in 24ms (ActiveRecord: 4.3ms)
It's been about 4+ months since I've done any rails, and back then I was only just beginning to learn it.
1) Does anyone know why I'm not getting anything back? I know it's something to do with my respond_with line of code in update, but I'm not sure exactly what.
2) Can someone clarify to me the difference between the SHOW respond_with and the CREATE respond_with. I recall having an issue grasping this back then, and obviously now.
SHOW
respond_with user_list, serializer: Api::V1::UserListSerializer
CREATE
respond_with :api, :v1, #current_user, user_list, serializer: Api::V1::UserListSerializer
a) Why does create require :api and :v1 first, but show does not?
b) Why does create require the #current_user, but show does not?
Appendix: Here is my Serializer for reference
class Api::V1::UserListSerializer < ActiveModel::Serializer
attributes :id, :user_id, :type_id, :title
has_many :items, embed: :ids
end
I know this is 2 years too late, but after some digging, I found the empty response with the 204 is intentional (as mentioned above). If you use respond_with this will always be the case. A workaround would be to use render instead (example below):
class Api::V1::ItemsController < ApplicationController
respond_to :json
...
def update
#item = Item.find(params[:id]
if #item
#item.update_attribute(item_params)
render json: #item
end
end
...
end
You're not supposed to get anything back other than the 204. Any intelligent client does not need to receive back the data it just sent you -- it needs only confirmation that the data was persisted.
Do not mistakenly pass your class Api::V1::UserListSerializer as a key/value pair (Hash form). You will get an error including the text class or module needed. It should look like this:
serialize :some_array, Api::V1::UserListSerializer
Or, perhaps clearer would be:
serialize(:some_array, Api::V1::UserListSerializer)
You miss one param and you are rendering an object class with no content : 204 - No Content
That may seem obvious, but it is common to be in the habit of passing things as a key/value pair.
One improve:
before_action :authenticate_user, only: [:create, :show, :update, ...]
https://apidock.com/rails/ActiveRecord/Base/serialize/class
def update
#item = Item.find(params[:id])
respond_with(:api, :v1, #item) do |format|
if #item.update(item_params)
format.json { render json: #item}
else
format.json { render json: {error: #item.errors.full_messages}}
end
end
end
I'm trying to render JSON to print the page. It's a method that it updates an attribute and render a view to print, like this:
class Admin::RequestsController < Admin::ApplicationController
def print
#request.print = 1
respond_to do |format|
if #request.save
format.json { render json: #request.as_json(only: [:id, :number]) }
end
end
authorize #request
end
end
This way, after clicking on the print icon, it opens a new window with options to print, but it lists nothing. Only [Object object].
I'm trying this to render print.json.builder to print, but it doesn't work:
json.(#request, :id, :number)
json.items do |json|
json.(#request.items, :item, :request_product)
end
I don't know if it's correct.
Why are you doing authorize #request at the end of your method?
Ruby implicitly returns whatever the last line of your method is, so you want your format.json method call to be the last thing you do. Could you move the authorize to be the first method call?
If it does need to be at the end, you can try this:
def print
#request.print = 1
result = nil
respond_to do |format|
if #request.save
result = format.json { render json: #request.as_json(only: [:id, :number]) }
end
end
authorize #request
result
end