I'm using ActiveModel Serializers to serialize my models and I'm constantly in need to create a new serializer in order to satisfy the needs of an controller without including unnecessary information into another.
class ContactGroupSerializer < ActiveModel::Serializer
attributes :id, :name, :contacts, :contacts_count,
:company_id, :user_id
def contacts_count
object.contacts.count
end
end
Is there a way to define a single serializer, such as the one above, and them dinamically select which attributes to be included on my controller response?
class ContactsGroupsController < ApplicationController
def index
...
render json: #contact_groups // here I would like to return only id and name, for example
end
end
I know I can achieve that by creating another serializer, but I wouldn't like to.
Well, you can just define a method in your application_controller.rb to which you can pass all your objects to be rendered with array of methods to be returned as response..like for example,
def response_for(object, methods = [:id])
if object.blank?
head :no_content
elsif object.errors.any?
render json: { errors: object.errors.messages }, status: 422
else
render json: build_hash_for(object, methods), status: 200
end
end
private #or in your `application_helper.rb`
def build_hash_for(object, methods)
methods.inject({}) do |hash, method|
hash.merge!(method => object.send(method))
end
end
In your particular case above, you can just
class ContactsGroupsController < ApplicationController
def index
...
response_for #contact_groups, [:id, :name]
end
end
Related
So I am working on creating a playercard, which is basically a profile page for a user. The issue I am having on the backend is my private method playercard_params is only returning user_id, and not all the information inputted into the form...although regular params shows all the data needed to create the playercard. I thought the issue might be on the frontend, but working my way backwards came to the conclusion the issue is here on the backend.
Here is my controller:
class Api::V1::PlayercardController < ApplicationController
before_action :set_user
def index
if params[:user_id]
#playercard = #user.playercard
else
#playercard = Playercard.all
end
render json: #playercard
end
def show
#playercard = Playercard.find(params[:id])
render json: #playercard
end
def create
#playercard = Playercard.new(playercard_params)
binding.pry
if #playercard.save
render json: #user
else
render json: {
error: #playercard.errors.full_messages.to_sentence
}
end
end
def update
#playercard = Playercard.find(params[:id])
if #playercard.update(playercard_params)
render json: #playercard
else
render json: {
error: #playercard.errors.full_messages.to_sentence
}
end
end
private
def playercard_params
params.require(:playercard).permit(:player_nickname, :player_height_in_feet, :player_height_in_inches, :player_weight, :player_age, :player_fav_player, :user_id)
end
def set_user
#user = User.find(params[:user_id])
end
end
My playercard model:
class Playercard < ApplicationRecord
belongs_to :user
validates :player_nickname, :player_height_in_feet, :player_height_in_inches, :player_weight, :player_age, :player_fav_player, presence: true
end
and the serializer if that helps:
class PlayercardSerializer < ActiveModel::Serializer
attributes :id, :player_nickname, :player_height_in_feet, :player_height_in_inches, :player_weight, :player_age, :player_fav_player
belongs_to :user
end
Here are my params:
<ActionController::Parameters {"playerNickname"=>"white mamba", "playerHeightFeet"=>"6", "playerHeightInches"=>"3", "playerAge"=>"30", "playerWeight"=>"170", "playerFavPlayer"=>"Kobe", "user_id"=>"1", "controller"=>"api/v1/playercard", "action"=>"create", "playercard"=><ActionController::Parameters {"user_id"=>1} permitted: false>} permitted: false>
When I submit the form on the front end, I get errors saying each field is empty...in the pry, if I type playercard_params, only user_id shows up (with the correct id)
I solved the issue by lining up the naming convention for the attributes with the front-end and back-end. And it worked!
Thank you #jvillian for the insight!!
I have a requirement in rails api application. client can have many orders and each order belongs to a client.
class Client < ApplicationRecord
has_many :orders
end
my order.rb is
class Order < ApplicationRecord
belongs_to :client, dependent: :destroy
accepts_nested_attributes_for :client
validates_presence_of :order_amount, :service_amount, :miners_amount
end
I have a route exposed /place_order and which creates client and orders.
class OrderProcessingController < ApplicationController
def place_order
#order = Order.new(order_processing_params)
if #order.save
render json: #order
else
render json: #order.errors.full_messages
end
end
private
def order_processing_params
params.require(:order).permit(:order_amount, :service_amount, :miners_amount, client_attributes: [:name, :email, :phone_number, :date])
end
end
Everything works fine so far. Now my requirement is, i have to check the client is already present in client table. if yes add the client_id for the orders and create new order. I don't want to create new client and order every time.
how can i achieve the same in before_filter or something like that. get the client from client params and if the client present delete the params key from incoming params ???
the post data for place_order is as follows
{
"order" : {
"order_amount" : "10000",
"service_amount" : "1000",
"miners_amount" : "10000",
"client_attributes": {
"name": "Ajith",
"email": "ajith#gmail.com",
"phone_number": "12231321312",
"date": "12/12/12"
}
}
}
Thanks in advance,
Ajith
The below code is not tested, mostly your approach should be around this
class OrderProcessingController < ApplicationController
before_action :find_client, only: [:place_order]
def place_order
#order = #client.orders.new(order_processing_params)
if #order.save
render json: #order
else
render json: #order.errors.full_messages
end
end
private
def order_processing_params
params.require(:order).permit(:order_amount, :service_amount, :miners_amount, client_attributes: [:name, :email, :phone_number, :date])
end
def find_client
#client = Client.find_or_create_by(email: params[:order][:client_attributes][:email])
#below line can be improved using a method, see the last line if later you want, never update a primary key which is email in bulk params
#client.update_attributes(name: params[:order][:client_attributes][:name], phone_number: params[:order][:client_attributes][:phone_number], date: params[:order][:client_attributes][:date])
end
#def get_client_params
# params.require(:order)
#end
end
I tried below approach to get a solution. not very sure that this is the right way to approach the problem
class OrderProcessingController < ApplicationController
before_action :find_client, only: :place_order
def place_order
if #client.present?
#order = #client.orders.build(order_processing_params)
else
#order = Order.new(order_processing_params)
end
if #order.save
render json: #order
else
render json: #order.errors.full_messages
end
end
private
def order_processing_params
params.require(:order).permit(:order_amount, :service_amount, :miners_amount, client_attributes: [:name, :email, :phone_number, :date])
end
def find_client
begin
#client = Client.find_by_email(params[:order][:client_attributes][:email])
rescue
nil
end
end
end
Thanks,
Ajith
I'm using will_paginate and api-pagination to paginate my data and active_model_serializers for JSON serialization.
I want to send the total entries of the resource to the client where I'm using AngularJS.
Controller code
def index
comp_id = params[:company_id]
cat_id = params[:category_id]
if comp_id
if comp_id && cat_id
product = Product.where(:company_id => comp_id, :category_id => cat_id)
else
product = Product.where(:company_id => comp_id)
end
paginate json: product,meta: pagination_dict(product), status: 200
else
render json: { errors: "Company ID is NULL" }, status: 422
end
end
Serializer
class ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :mrp, :sp, :cp, :stocks, :isPublished
has_one :category
end
How can I include the total_count in my response?
Update
I tried adding the following method in my base controller as per this doc, however I'm getting undefined method error for all the methods defined.
class ApplicationController < ActionController::Base
include Authenticable
include Rails::Pagination
rescue_from ActiveRecord::RecordNotFound, :with => :render_404
protect_from_forgery with: :null_session
def render_404
render json: { errors: 'The requested resource cannot be found' }, status: 404
end
def pagination_dict(collection)
{
#current_page: collection.current_page,
#next_page: collection.next_page,
#prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
total_pages: collection.total_pages,
total_count: collection.total_count
}
end
end
These methods (total_pages, total_entries...) work only on paginated ActiveRecord relation.
For example, Product.where(company_id: comp_id).page(1).total_pages returns the number of total pages, because the relation uses pagination.
In your case, why not just use ActiveRecord's count method to get the total number of entries?
paginate json: product, meta: product.count, status: 200
I have the following code in my account.rb model file:
class Account < ActiveRecord::Base
alias_attribute :id, :accountID
alias_attribute :name, :awzAccountName
alias_attribute :description, :awzAccountDescription
end
And the following code in the index method from my accounts_controller.rb file:
def index
#accounts = Account.all
if params["page"]
page = params["page"]
items_per_page = params["per_page"]
render :json => {:total => #accounts.count,:accounts => #accounts.page(page).per(items_per_page) }
else
render json: #accounts
end
end
As expected, render json: #accounts returns a result set that contains the alias_attribute column names defined in the model file. However, the render :json => {:total => #accounts.count,:accounts => #accounts.page(page).per(items_per_page) } code returns a result set that contains the original column names. Is there any way to change this so that the alias_attribute column names are used?
I wouldn't expect render json: #accounts to include the aliased attributes at all. The alias_attribute just gives you the luxury of referring to the attribute with another name - it doesn't replace the original name at all.
If you do want to include the aliases in your json output for a model you can override as_json and add those methods explicitly:
def as_json(options = {})
options[:methods] ||= []
options[:methods] += [:name, :description]
super(options)
end
(I've deliberately omitted :id as that may be a special case - not entirely sure and can't test locally at the moment)
I was able to solve this by overwriting serializable_hash method.
def serializable_hash(options = {})
options[:methods] ||= []
options[:methods] += [:name, :description]
super(options)
end
You can achieve the same result by passing methods argument to as_json without changing your default serialization of your models. like this:
render json: #accounts.as_json(methods: [:name, :description])
I am using Thinking Sphinx to run searches and I get the appropriate ActiveRecord Models fine. The problem is, I want to create an appropriate link path and text on each model, then send the info to the browser in the form of JSON, via AJAX. I am using the following to build those link attributes:
In the controller:
class FindController < ApplicationController
def tag_results
#results = ThinkingSphinx.search(params[:terms])
#results.each do |result|
result.build_ajax_response
end
respond_to do |format|
format.html
format.json { render :json => #results }
end
end
end
In the model:
class TaggedItem < ActiveRecord::Base
attr_accessible :name
attr_accessor :search_link, :search_text
def build_ajax_response
self.search_link = Rails.application.routes.url_helpers.tagged_item_path(self.id)
self.search_text = self.name
end
end
The resulting json object doesn't have either of the search_* attributes listed, much less have a value for them. I've tried using #search_link as well as just search_link in the build_ajax_response method.
Am I doing this wrong? Could there be something else interfering?
Rails' default to_json doesn't know about those extra non active record attributes you've added. The easiest possible thing is probably to specify them as extra methods to include:
format.json { render :json => #results.to_json(:methods => [:search_link, :search_text]) }