this is my controller
class BooksController < ApplicationController
def create
#book = Book.new(book_params)
if #book.save
render json: #book, status: 201
else
render json: { error: "check attributes again", status: 400 }, status: 400
end
end
private
def book_params
params.require(:book).permit(:author, :categories, :publisher, :title)
end
end
i am passing prams like this
{ "book":{
"author": "some one",
"categories": "some thing",
"publisher": "some publisher",
"title": "some thing my own"
}
}
I am getting the above error what is the wrong in it. Any ideas? I am using sqlite3 data base and webric server.
Check below log file.
"your rails directory"/logs/development.log
And one more point, you should write 'puts' debug like below, because 'puts' results display above log file.
def create
puts 'params => ' + params
#book = Book.new(book_params)
#Nani, you have to find in your logs something similar to this:
Started POST "/books" for 127.0.0.1 at 2016-02-18 18:58:17 +0200
Processing by BooksController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"....", "book"=>{....}}
And here you need to check if you expecting correct parameters in your controller.
Related
What I want to come true
Show I want to return two JSON data after receiving a request. What should I do now.
One is the received parameter data. The second is the related data.
post_items is the associated data.
Code
def show
post = Post.find(params[:id])
content = post.post_items # I want to return this
render json: post // content...
end
What I tried myself and error
① Tworender :json
def show
post = Post.find(params[:id])
content = post.post_items
render json: post, status: 200
render json: content, status: 200 //add
end
error
AbstractController::DoubleRenderError (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".)
Now I know that I can't render twice
② I used respond_to
def show
post = Post.find(params[:id])
content = post.post_items
respond_to do |format|
format.json { render json: post }
format.json { render json: content }
end
error
NoMethodError (undefined method `respond_to' for #<Api::V1::PostsController:0x00005612037f95c0>
api_1 | Did you mean? respond_to?
api_1 | respond_with
api_1 | responder
api_1 | responder?
api_1 | responder=):
api_1 |
The easiest solution is to return one json object with your two sub-objects posts, content under different keys.
render json: {"post": post, "content": content}, status: 200
Returns the json
{"post": ... , "content": ... }
In this case,
Add this line to your application's Gemfile:
gem 'active_model_serializers', '~> 0.10.2'
And then execute:
$ bundle
And then generate model serializers
rails g serializer post
All serializer .rb files grouped in app/serializers
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :description
end
if you want content data with post then, add association to serializer
class PostSerializer < ActiveModel::Serializer
has_many :post_items
attributes :id, :title, :description, :post_items
end
In controller,
def show
post = Post.find(params[:id])
render json: post
end
It will return the post data and related post_items data
{
id: 1,
title: "",
description: "",
post_items: {
{
id: 1,
name: ""
}
}
}
I am trying to setup a Rails API backend for a bookstore project, and the frontend will be react. The backend has only one model for now which is the Book model. In the at the frontend I will like to filter the books based on their category. I want to setup the filtering at the backend so that when I select a dropdown at the frontend, only books that fit a particular category will be displayed. So far here is what I have at the backend:
Book model
class Book < ApplicationRecord
validates :title, :category, presence: true
scope :categorized, -> (category) { where("category LIKE ?", "#{category}" ) }
end
my book controller looks like this:
Book controller
class Api::V1::BooksController < Api::V1::ApiController
before_action :set_book, only: %i[show update destroy]
def index
#books = Book.all
render json: #books
end
def categorized
#category = params[:book]
#books = Book.categorized(#category)
render json: #books
end
def show
render json: #book
end
def create
#book = Book.new(book_params)
if #book.save
render json: #book, status: :created
else
render json: #book.errors, status: :unprocessable_entity
end
end
def update
if #book.update(book_params)
render json: #book
else
render json: #book.errors, status: :unprocessable_entity
end
end
def destroy
#book.destroy
end
private
def set_book
#book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :category)
end
end
Routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :books do
collection do
get :categorized
end
end
end
end
end
Sample Data
Here is a sample data I get from the API when I query for all books in the database:
[
{
"id": 1,
"title": "I Know Why the Caged Bird Sings",
"category": "Fable"
},
{
"id": 2,
"title": "His Dark Materials",
"category": "Speech"
},
{
"id": 3,
"title": "To Say Nothing of the Dog",
"category": "Fable"
},
{
"id": 4,
"title": "An Acceptable Time",
"category": "Science fiction"
},
{
"id": 5,
"title": "A Scanner Darkly",
"category": "Suspense/Thriller"
},
{
"id": 6,
"title": "The Golden Bowl",
"category": "Science fiction"
}
]
My interest is I want to see only "Science fiction" books or any other category I choose only
using scopes. The categories are not limited to what is shown in the sample data; infact they
are likely to increase. The configuration I have above in the model is not getting what I
need. When I use postman, I do not get any result. The best I get is an empty array.
scope :categorized, -> (category) { where("category LIKE ?", "#{category}" ) }
Are you sure you need this scope for only one place?
Why don’t just filter in controller?
Second thought is using Book.where(category: category) which will produce “WHERE CATEGORY = ?” instead of “WHERE category LIKE”
Third - may be you can get rid of categorized route and just use index with optional param? You can also add pagination later in index controller - where...page(page).per(per)
Fourth, there is a good practice of using instance variable accessor methods instead of variable itself:
def set_book
#book = Book.find(params[:id])
end
To
def book
#book ||= Book.find(params[:id])
end
Also find method raises exception. You might want to rescue from it on class level. You can also use book.update! And book.save! in other methods and rescue from them as well - then you won’t need those if/else branches.
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
I have an rails app with json api. So far I can create single objects via POST request.
It's fairly simple:
def create
customer = Customer.new(customer_params)
if customer.save
render json: customer, status: 201
else
render json: customer.errors, status: 422
end
end
and:
private
def customer_params
params.require(:customer).permit(:name, :city)
end
Now I want to create multiple customers by passing an array in my http request. Like this:
{
"customer": [
{
"name": "foo",
"city": "New York"
},
{
"name": "bar",
"city": "Chicago"
}
]
}
However, I don't know how to approach this. The first issue is that my strong parameters function doesn't accept arrays.
Is there a way to use strong parameters and let me loop through the array?
I would see it as a new controller method
something like:
def multi_create
render json: customer.errors, status: 422 and return unless params[:customers]
all_created = true
customers = []
params[:customers].each do |customer_params|
customer = Customer.create(name: customer_params[:name], city: customer_params[:city])
customers << customer
all_created &&= customer.valid?
end
if all_created
render json: customers, status: 201
else
render json: customers.map(&:errors), status: 422
end
end
You also need to add the route. then you could post your json to that route with the change that the outermost key should be customers.
I would not run this code without any changes but you get the general idea. And you can refactor it to your liking.
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.