Exclude data from as_json method - ruby-on-rails

My Model includes some country informations
class MyModel
include Mongoid::Document
field :name, type: String
field :country, as: :country, type: Country
end
MyModel.first.country
#<Country:0x007fc6a5d5f278 #data={"continent"=>"Asia",
"alpha2"=>"TH",
"alpha3"=>"THA",
"country_code"=>"66",
"currency"=>"THB",
"international_prefix"=>"001",
"ioc"=>"THA", "latitude"=>"15 00 N",
"longitude"=>"100 00 E", "name"=>"Thailand",
"names"=>["Thailand", "Thaïlande", "Tailandia", "タイ"],
"translations"=>{"en"=>"Thailand",
"it"=>"Tailandia",
"de"=>"Thailand",
"fr"=>"Thaïlande",
"es"=>"Tailandia",
"ja"=>"タイ",
"nl"=>"Thailand",
"ru"=>"Таиланд"},
"national_destination_code_lengths"=>[2],
"national_number_lengths"=>[9, 10],
"national_prefix"=>"0", "number"=>"764",
"region"=>"Asia", "subregion"=>"South-Eastern Asia",
"un_locode"=>"TH", "languages"=>["th"], "nationality"=>"Thai"}>
Calling MyModel.first.to_json(only: [:name, :country]) should only return alpha2, translations and names
How can I achieve that?
I try to avoid to write an extra method for that.
EDIT:
Expected Output something like this:
MyModel.first
{"name": "ModelName",
"country": {"alpha2"=> "TH",
"name" => "Thailand",
"names"=> ["Thailand", "Thaïlande", "Tailandia", "タイ"]
}

You can override as_json method for your Model to get the desired result.
class MyModel
def as_json(options = {})
super(only: [:name]).merge(
country: country.data.slice("alpha2", "unofficial_names", "translations")
)
end
end
Now you can get the data by calling as_json on Model
MyModel.first.as_json
Hope it helps !

Related

Update has_many attributes with irregular params

I have model LoanPlan and Career, they are associated by a join_table
The request params from another frontend developer will be like this
"loan_plan" => {
"id" => 32,
"careers" => [
[0] {
"id" => 8,
},
[1] {
"id" => 9,
}
]
},
However, I got ActiveRecord::AssociationTypeMismatch: Career(#70198754219580) expected, got ActionController::Parameters(#70198701106200) in the update method
def update
#loan_plan.update(loan_plan_params)
end
When I tried to update the loan_plan model with careers params, it expects the params["careers"] should be careers object of a array instead of ids of a array.
So my workround is to manually fectch the careers objects of a array and replace the sanitized params.
It seems dirty and smells bad, any better solution in Rails way? Thanks
def loan_plan_params
# params.fetch(:loan_plan, {})
cleaned_params = params.require(:loan_plan).permit(
:id,
:name,
{:careers=>:id}
)
cleaned_params["careers"] = Career.find(cleaned_params["careers"].map{|t| t["id"]})
cleaned_params
end
model
class LoanPlan < ActiveRecord::Base
has_and_belongs_to_many :careers
accepts_nested_attributes_for :careers
end
In Rails way, params should be
"loan_plan" => {
"id" => "32",
"career_ids" => ["8", "9"]
}
and the strong parameter loan_plan_params should be
def loan_plan_params
params.require(:loan_plan).permit(
:id,
:name,
:career_ids => []
)
end

Rails to_json belongs_to object

I have two models: Cabinet and Workplace.
class Cabinet < ActiveRecord::Base
def as_json(options={})
options.merge!({except: [:created_at, :updated_at]})
super(options)
end
end
class Workplace < ActiveRecord::Base
belongs_to :cabinet
def as_json(options = {})
options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet)
super(options)
end
end
When I called Cabinet.first.to_json I get
{
id: 1,
cabinet: "100"
}
but when I called Workplace.first.to_json id get
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
Why this? Thanks and sorry for my english :)
Not sure if I am following you, but do you want to get just attributes from Workplace model, and not Cabinet data when you do Workplace.first.to_json?
I think it is because you include cabinet in as_json method configuration as explained here.
You should either remove it or do this:
Workplace.first.attributes.to_json
Let me know if I am missing something from your question.
Let's assume that your model Cabinet has :id, :cabinet, :created_at, :updated_at attributes and Workplace has :id, :name, :cabinet_id, .....
Now, if you try to fire Cabinet.first.to_json, ofcourse it will render the following:
{
id: 1,
cabinet: "100"
}
becuase that is the attributes belongs to Cabinet model. Then you also added these line of code options.merge!({except: [:created_at, :updated_at]}) that's why it only renders :id and :name attributes. And if you try to fire Workplace.first.to_json then it will render:
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
because, of these options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet). You include the model Cabinet so it will automatically added to your json.

Set Virtual Attribute Rails Active Model

How do I set a virtual attribute that sets the first and last name, when I call Quote.new()?
The before_save :assign_name method does not seem work. I get an error
NoMethodError: undefined method `before_save' for Quote:Class
CONTROLLER:
quote = {name: "John Doe", City: "New York"}
Quote.new(quote)
MODEL:
class Quote
include ActiveModel::Model
before_save :assign_name
attr_accessor :name, :first, :last, :city
def assign_name
title_split = self.name.split(" / ")
self.first = title_split[0]
self.last = title_split[1]
end
end
You can use something like this
class Quote
include ActiveModel::Model
attr_accessor :name, :first, :last, :city
def initialize(attributes={})
super
assign_name(name)
end
def assign_name(name)
title_split = name.split(" / ")
self.first = title_split[0]
self.last = title_split[1]
end
end
Also link to documentation here
before_save is defined in ActiveRecord. You need to let your class inherit from ActiveRecord::Base as the following:
class Quote < ActiveRecord::Base
end
And if you put the method in "before_save" callback, that means the method will be called only when Quote#save is executed. For example,
quote = {name: "John Doe", City: "New York"}
q = Quote.new(quote)
q.save

Return custom columns in Rails ActiveRecord Query

I am querying my ActiveRecords in rails with the following:
result = MyObj.where({customer: current_id}).as_json()
There are two columns returned:
result = [{id:1, name: "david", last_name: "Smith:"}]
I would like create a third column (which will not be saved to the DB) like so:
result = [{id:1, name: "David", last_name: "Smith:", full_name:"David Smith"}]
Is this possible within the WHERE query?
Add a full_name method to your MyObj model, then pass methods: :full_name to the as_json method:
class MyObj
def full_name
"{name} #{last_name}"
end
end
result = MyObj.where({customer: current_id}).as_json(methods: :full_name)
From the documentation for as_json:
To include the result of some method calls on the model use :methods:
user.as_json(methods: :permalink)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "permalink" => "1-konata-izumi" }
Or alternately, you could override as_json on the model to include full_name by default:
class MyObj
def full_name
"{name} #{last_name}"
end
def as_json(options={})
super({methods: :full_name}.merge options)
end
end
Sure. Override the method in your model...
class MyObj < ActiveRecord::Base
def full_name
"#{name} #{last_name}"
end
def as_json options={}
{
id: id,
name: name,
last_name: last_name,
full_name: full_name
}
end
end
Quick and dirty just manipulate the results you get back
result = MyObj.where({customer: current_id})
result.map{|customer| "full_name: #{customer.first_name + customer.last_name}" }
But be careful of nil values.

Have to_json return a mongoid as a string

In my Rails API, I'd like a Mongo object to return as a JSON string with the Mongo UID as an "id" property rather than as an "_id" object.
I want my API to return the following JSON:
{
"id": "536268a06d2d7019ba000000",
"created_at": null,
}
Instead of:
{
"_id": {
"$oid": "536268a06d2d7019ba000000"
},
"created_at": null,
}
My model code is:
class Profile
include Mongoid::Document
field :name, type: String
def to_json(options={})
#what to do here?
# options[:except] ||= :_id #%w(_id)
super(options)
end
end
You can monkey patch Moped::BSON::ObjectId:
module Moped
module BSON
class ObjectId
def to_json(*)
to_s.to_json
end
def as_json(*)
to_s.as_json
end
end
end
end
to take care of the $oid stuff and then Mongoid::Document to convert _id to id:
module Mongoid
module Document
def serializable_hash(options = nil)
h = super(options)
h['id'] = h.delete('_id') if(h.has_key?('_id'))
h
end
end
end
That will make all of your Mongoid objects behave sensibly.
For guys using Mongoid 4+ use this,
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end
Reference
You can change the data in as_json method, while data is a hash:
class Profile
include Mongoid::Document
field :name, type: String
def as_json(*args)
res = super
res["id"] = res.delete("_id").to_s
res
end
end
p = Profile.new
p.to_json
result:
{
"id": "536268a06d2d7019ba000000",
...
}
Use for example:
user = collection.find_one(...)
user['_id'] = user['_id'].to_s
user.to_json
this return
{
"_id": "54ed1e9896188813b0000001"
}
class Profile
include Mongoid::Document
field :name, type: String
def to_json
as_json(except: :_id).merge(id: id.to_s).to_json
end
end
If you don't want to change default behavior of MongoId, just convert result of as_json.
profile.as_json.map{|k,v| [k, v.is_a?(BSON::ObjectId) ? v.to_s : v]}.to_h
Also, this convert other BSON::ObjectId like user_id.
# config/initializers/mongoid.rb
# convert object key "_id" to "id" and remove "_id" from displayed attributes on mongoid documents when represented as JSON
module Mongoid
module Document
def as_json(options={})
attrs = super(options)
id = {id: attrs["_id"].to_s}
attrs.delete("_id")
id.merge(attrs)
end
end
end
# converts object ids from BSON type object id to plain old string
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end

Resources