I am using ActiveModelSerialziers in my Rails API and it works like a charm but 1 of my functions seems to override it, and only returns my model (without any of the associations). The same model returns properly if a different function is called.
def getClient
type = params[:type]
if type == 'user'
#client = Client.find_by(user_id: params[:id])
else
#client = Client.find_by(id: params[:id])
end
render json: { success: true, response: #client }
end
only returns the client without the associations and the serializer is:
class API::ClientSerializer < ActiveModel::Serializer
attributes :name, :age, :address
belongs_to :user
has_many :check_ins
has_many :client_docs
has_many :payments
end
While the same model (Client) returns properly (with associations) when the following function is called:
def show
model_name = params[:model].classify
item = model_name.constantize.find_by(id: params[:id])
render json: item, status: :ok
end
Why might this happen?
You are missing adapter: :json from the rander statement:
render json: item, adapter: :json, status: :ok
Related
Problem
Our API shall return JSON objects (but not JSON API).
How can we change the ActiveModel::Serializer to create a JSON object with data property set to null?
The response we get for an empty (has_one relationship) resource is NULL, but we expect it to be in a JSON format {data: NULL} just as we receive for a non-empty resource {data: {...}}, or for a list (has_many relationship) resource {data: []}.
What we have tried
We use ActiveModel::Serialiser and specify the key to be named "data" instead of the resource name (similar to JSON API, but the content of data is a direct JSON representation of the entity).
Models:
class User < ApplicationRecord
has_one :profile
def serializer_class
V1::UserSerializer
end
end
class Profile < ApplicationRecord
belongs_to :user
def serializer_class
V1::ProfileSerializer
end
end
Serializers:
class ApplicationSerializer < ActiveModel::Serializer
def json_key
'data'
end
end
class UserSerializer < ApplicationSerializer
attributes :id, :created_at, :updated_at #we do not include the profile here
end
class ProfileSerializer < ApplicationSerializer
attributes :id, :created_at, :updated_at
end
Controllers:
class ProfilesController < ::ApplicationController
before_action :authenticate_user!
def show
#profile = current_user.profile
render json: #profile
end
end
Responses:
We get a (for us wrong) response for an empty resource (GET /profile):
NULL
We correctly get a response for a non-empty resource, it looks like this (not JSON API):
{
data: {
id: ...,
createdAt: ...,
updatedAt: ...
}
}
What we would like
We expect the response to be formatted like this in case that there is no entity associated (yet):
{
data: null
}
I found this (workaround) solution solves the problem:
def show
#profile = current_user.profile
if #profile
render json: #profile
else
render json: { data: nil }
end
end
We decided to go for this more strict solution, where we respond with 404 for an empty resource.
def show
#profile = current_user.profile
raise ActiveRecord::RecordNotFound unless #profile
render json: #profile
end
And in the ApplicationController we add this to handle the exception:
rescue_from ActiveRecord::RecordNotFound do |exception|
render json: Api::ErrorSerializer.serialize(:not_found, 'Not Found'),
status: :not_found
end
I am new in Ruby on Rails. I am making a Rails API using Rails 5.1, active record serializer, doorkeeper and devise gem.
I have an Order table and it has many products. The relation between order and product is many-to-many.
Order model:
class Order < ApplicationRecord
validates_presence_of :brute, :net
has_and_belongs_to_many :products
end
Product model:
class Product < ApplicationRecord
belongs_to :category
validates_presence_of :name, :price
validates_uniqueness_of :name
has_and_belongs_to_many :orders
end
I have a join table named orders_products.
Order serializer:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at, :products
def products
object.products.map do |product|
ProductSerializer.new(product, scope: scope, root: false, event: object)
end
end
end
Product serializer:
class ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :price, :description
has_one :category
end
Order controller:
module Api
class OrdersController < ApiController
before_action :set_order, only: [:show, :update, :destroy]
# GET /api/orders
def index
#orders = Order.all
render json: #orders
end
# GET /api/orders/1
def show
render json: #order
end
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /api/orders/1
def update
if #order.present?
if #order.update(order_params)
render json: #order
else
render json: #order.errors, status: :unprocessable_entity
end
end
end
# DELETE /api/orders/1
def destroy
#order.destroy if #order.present?
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
#order = Order.find(params[:id])
rescue ActiveRecord::RecordNotFound
Rails.logger.error{ 'Order record is not found' }
nil
end
# Only allow a trusted parameter "white list" through.
def order_params
params.require(:order).permit(:discount, :brute, :net, :payed, :payed_at, product_ids: [])
end
end
end
When I post some order json data from API generator app like Postman/Insomnia, Order is being saved in orders table but no data saved in orders_products join table.
My request(POST http://localhost:3000/api/orders) of order json:
{
"discount": 110,
"brute": 100,
"net": 200,
"payed": null,
"payed_at": null,
"product_ids": [3]
}
I try to find the solution but I failed.
Finally I have solved in your problem.Just add an attribute in your model.
Order Model:
class Order < ApplicationRecord
attribute :product_ids
validates_presence_of :brute, :net
has_and_belongs_to_many :products
end
Order Serializer:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at
has_many :products
end
And create method in your order api:
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
# Find products
#products = Product.where(id: order_params[:product_ids])
# Create join table records
#products.each { |product| product.orders << #order }
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
I have tested in locally and it works! Happy Programming :)
As far as I know, Rails doesn't automatically handle creating the join records when given a list of ids. Therefore when you're calling #order = Order.new(order_params) and expecting it to know how to handle product_ids: [3], it's just ignoring it.
If you modify your create endpoint with the below, you should see the join records being created.
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
# Find products
#products = Product.where(id: order_params[:product_ids])
# Create join table records
#products.each { |product| product.orders << order }
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
This is just one possible solution that doesn't do any error checking. Depending how secure and robust your application needs to be you may need to create a service that wraps this and handles validating that products are found before creating the order and associating the records.
EDIT: OrderSerializer
Once you've verified that the join table records are being created properly. Check that your serializers are working, they have great documentation. I believe you can swap out your current products method in the OrderSerializer with this:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at, :products
def products
object.products.map do |product|
ProductSerializer.new(product).serializable_hash
end
end
end
i use rails 5 , simple form. in my app there is a Category model and there is a OnlineProduct model. i dont know why when i want to add some categories to my OnlineProduct association table remain empty and don't change.
Category model:
class Category < ApplicationRecord
has_ancestry
has_and_belongs_to_many :internet_products
end
InternetProduct model:
class InternetProduct < ApplicationRecord
belongs_to :user
belongs_to :business
has_and_belongs_to_many :categories
end
InternetProduct controller:
def new
#internet_product = InternetProduct.new
end
def create
#internet_product = InternetProduct.new(internet_product_params)
respond_to do |format|
if #internet_product.save
format.html { redirect_to #internet_product, notice: 'Internet product was successfully created.' }
format.json { render :show, status: :created, location: #internet_product }
else
format.html { render :new }
format.json { render json: #internet_product.errors, status: :unprocessable_entity }
end
end
end
private:
def internet_product_params
params.require(:internet_product).permit(:name, :description, :mainpic, :terms_of_use,
:real_price, :price_discount, :percent_discount,
:start_date, :expire_date, :couponŲlimitation, :slung,
:title, :meta_data, :meta_keyword, :enability, :status,
:like, :free_delivery, :garanty, :waranty, :money_back,
:user_id, :business_id,
categoriesŲattributes: [:id, :title])
end
and in the view only the part of who relate to categories :
<%= f.association :categories %>
all the categories list in view (form) but when i select some of them not save in database. in rails console i do this
p = InternetProduct.find(5)
p.categories = Category.find(1,2,3)
this save to database without any problem, what should i do ?
tanks for reading this
I found solution to solve this. when we use has_and_belong_to_many or any other relation , if you want to use collection select in simple_form , in the model also should be add this command for nesting form
accepts_nested_attributes_for :categories
also in the controller in related method for example in the new we should
def new
#internet_product = InternetProduct.new
#internet_product.categories.build
end
I have a Match model with 2 players fields that have a belongs_to association with the User model
Model
class Match < ApplicationRecord
belongs_to :player1, :class_name => 'User', :foreign_key => 'player1'
belongs_to :player2, :class_name => 'User', :foreign_key => 'player2'
end
When creating a Match via the API (using a Postman POST request) I tried passing the user_id of the players but got a TypeMismatch error indicating the controller expected a User object but got a Fixnum.
Looking at this line:
#match = Match.new(match_params)
the error makes sense, so I modified my default scaffold generated controllers to look like this instead:
def create
#match = Match.new
#match.player1 = User.find(params[:match][:player1])
#match.player2 = User.find(params[:match][:player2])
if #match.save
render json: #match, status: :created, location: #match
else
render json: #match.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /matches/1
def update
if #match.update( :player1 => User.find(params[:match][:player1]),
:player2 => User.find(params[:match][:player2])
)
render json: #match
else
render json: #match.errors, status: :unprocessable_entity
end
end
It works, but the solution seems "inelegant".
Is there a better way to pass values to a controller with a belongs_to association?
Could you please try changing your foreign_key in Match model from player1, player2 to player1_id, player2_id respectively, via database migrations. Because your foreign_key and belongs_to associations are same? Please let me know whether it works!
I have some troubles to make an association between 2 tables.
I have Users who can write Posts
Here is my migration file :
class LinkUsersAndPosts < ActiveRecord::Migration
def change
add_column :posts, :user_id, :integer
add_index :posts, :user_id
end
end
My models :
class Post < ActiveRecord::Base
attr_accessible :content, :title
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :email, :login, :password, :password_confirmation, :rights
has_many :posts
end
My controller :
class PostsController < ApplicationController
respond_to :json
def index
#posts = Post.includes(:user).all
respond_with #posts
end
def create
#post = Post.new(params[:post])
#post.user = current_user
if #post.save
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
end
end
The posts is correctly created, the user_id is set all is fine.
The problem is when i want to retrieve the list of posts including user, the list of posts is retrieve, but i havn't any data on the user except his id.
Response :
[
{
"content":"jksdjd",
"created_at":"2013-08-31T09:03:01Z",
"id":11,"title":"kdjs",
"updated_at":"2013-08-31T09:03:01Z",
"user_id":4
},
{
"content":"tez2",
"created_at":"2013-08-31T09:16:45Z",
"id":12,
"title":"test2",
"updated_at":"2013-08-31T09:16:45Z",
"user_id":4
}
]
By default a JSON response won't return any associated models. You need to specify the other models you want returned. So in your case, you can do this:
render json: #post => #post.to_json(:include => :user), status: :created, location: #post
Also see:
http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
Rails Object Relationships and JSON Rendering