This question already has answers here:
Rails 4 - Remove "created_at" and "updated_at" from render
(2 answers)
Closed 5 years ago.
I am new to Ruby, so this question might look stupid for you, yeah. But still...
I got a model Group, and when I run this
class GroupsController < ApplicationController
prepend SimpleCommand
...
def index
#group = Group.all
render json: #group
end
...
# some other stuff
...and then I get a response with array like this:
[
{
"id": 1020,
"name": "НІ-16-1",
"has_subgroups": true,
"created_at": "2017-09-29T10:14:08.000Z",
"updated_at": "2017-09-29T10:14:08.000Z"
}
...
]
but I don't want to have those two last fields, so my answer is
How to get rid of "created_at" and "updated_at" fields in a response?
UPD:
Found a great solution in original question I've accidentally made a duplicate for. Go ahead and read it
You could simply do:
class GroupsController < ApplicationController
prepend SimpleCommand
...
def index
render json: Group.pluck(:id, :name, :has_subgroups)
end
end
It may not matter to you, but I think this approach is faster because it doesn't instantiate all the Group objects.
Every time you serve an object to the view as JSON Rails internally calls the function as_json. One way to get rid of those two fields is to override that function in your Group model:
class Group < ActiveRecord::Base
def as_json(options={})
{ id: id,
name: name,
has_subgroups: has_subgroups
}
end
end
Or as #MrYoshiji suggested you could filter them out like so:
render json: #groups.to_json(except: [:created_at, :updated_at])
Rails come with jbuilder by default to generate the json response. You just have to find group/index.json.jbuilder and remove created_at and updated_at inside. And change your controller action to:
def index
#group = Group.all
end
or if you need to support other formats:
def index
#group = Group.all
respond_to do |format|
format.json
end
end
You can see you don't have to specify anything as Rails will use your jbuilder file by default.
Related
I have a controller action (favorites) in my Rails app that returns a JSON object with two keys (companies and jobs). Each key represents a collection of Company or JobDescription objects. What I want to know is if there is a clean way I can serialize both #companies and #jobs. Here is my code:
def favorites
#companies = current_user.companies
#jobs = current_user.job_descriptions
respond_to do |format|
format.html
format.json { render json: {companies: #companies, jobs: #jobs}, root: false }
end
end
I could always refactor my code into two separate JSON calls (one for jobs, one for companies), but I'd prefer to stick with a single call to favorites.
You can use Rails Presenters here!
So, you can have two presenters: CompaniesPresenter and JobsPresenter which will be responsible for building the #companies and jobs objects respectively.
So, in your controller, you would have something like:
#companies = CompaniesPresenter.new(current_user).companies
#jobs = JobsPresenter.new(current_user).job_descriptions
For example, your CompaniesPresenter would look like this:
class CompaniesPresenter
attr_reader :current_user
def initialize(current_user)
#current_user = current_user
end
def companies
# build the companies JSON here
end
end
Here is a tutorial with Rails Presenter Pattern that might be useful.
And, here is an useful video. Hope this helps.
This example works, are you just trying to change the json format? If so...
In the company or job model, you can add an as_json method and format the output as you want.
def as_json(options = {})
{ :name => name }
end
I have a Rails Controller who responds with JSON objects. Let's take this theoretical example :
respond_to :json
def index
respond_with Comment.all
end
This would respond with something like
[{"id":1,"comment_text":"Random text ", "user_id":1 ,"created_at":"2013-07-26T15:08:01.271Z","updated_at":"2013-07-26T15:08:01.271Z"}]
What i'm looking for is a "best practice" method to interfere with the formating of the json object and return something like this :
[{"id":1,"comment_text":"Random text ", "username": "John Doe", "user_id":1 ,"created_at":"3 hours ago"}]
As you can see, i'm adding a column that doesn't exist in the database model "username" , i'm taking out "updated_at" , and i'm formatting "created_at" to contain human readable text rather than a date.
Any thoughts anyone ?
Overwriting as_json or working with JSON ERB views can be cumbersome, that's why I prefer using ActiveModel Serializers (or RABL):
class CommentSerializer < ActiveModel::Serializer
include ActionView::Helpers::DateHelper
attributes :id, :created_at
def created_at
time_ago_in_words(object.created_at)
end
end
Look here for more information:
https://github.com/rails-api/active_model_serializers
https://github.com/nesquena/rabl
2 ways:
first: define a view, where you build and return an hash that you'll convert to json.
controller:
YourController < ApplicationController
respond_to :json
def index
#comments = Comment.all
end
end
view: index.json.erb
res = {
:comments => #comments.map do |x|
item_attrs = x.attributes
item_attrs["username"] = calculate_username
end
}
res.to_json.html_safe
second: use gem active_model_serializers
I'd redefine the as_json method of your model.
In your Comment model,
def username
"John Doe"
end
def time_ago
"3 hours ago"
end
def as_json(options={})
super(:methods => [:username, :time_ago], except: [:created_at, :updated_at])
end
You don't have to change your controller
Take a look at the documentation for as_json
In Topic model:
class Topic < ActiveRecord::Base
has_many :choices, :dependent => :destroy
accepts_nested_attributes_for :choices
attr_accessible :title, :choices
end
During a POST create, the params submitted is :choices, instead of :choices_attributes expected by Rails, and giving an error:
ActiveRecord::AssociationTypeMismatch (Choice(#70365943501680) expected,
got ActiveSupport::HashWithIndifferentAccess(#70365951899600)):
Is there a way to config accepts_nested_attributes_for to accept params passing as choices instead of choices_attributes in a JSON call?
Currently, I did the attributes creation in the controller (which seems not to be an elegant solution):
def create
choices = params[:topic].delete(:choices)
#topic = Topic.new(params[:topic])
if choices
choices.each do |choice|
#topic.choices.build(choice)
end
end
if #topic.save
render json: #topic, status: :created, location: #topic
else
render json: #topic.errors, status: :unprocessable_entity
end
end
This is an older question, but I just ran into the same problem. Is there any other way around this? It looks like that "_attributes" string is hardcoded in the nested_attributes.rb code (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L337).
Assigning "choices_attributes" to a property when submitting a form is fine, but what if it's being used for an API. In that case it just doesn't make sense.
Does anyone have a way around this or an alternative when passing JSON for an API?
Thanks.
UPDATE:
Well, since I haven't heard any updates on this I'm going to show how I'm getting around this right now. Being new to Rails, I'm open to suggestions, but this is the only way I can figure it out at the moment.
I created an adjust_for_nested_attributes method in my API base_controller.rb
def adjust_for_nested_attributes(attrs)
Array(attrs).each do |param|
if params[param].present?
params["#{param}_attributes"] = params[param]
params.delete(param)
end
end
end
This method basically converts any attributes that are passed in to #{attr}_attributes so that it works with accepts_nested_attributes_for.
Then in each controller that needs this functionality I added a before_action like so
before_action only: [:create] do
adjust_for_nested_attributes(:choices)
end
Right now I'm only worried about creation, but if you needed it for update you could add that into the 'only' clause of the before_action.
You can create method choices= in model as
def choices=(params)
self.choices_attributes = params
end
But you'll break your setter for choices association.
The best way is to modify your form to return choices_attributes instead choices
# Adds support for creating choices associations via `choices=value`
# This is in addition to `choices_attributes=value` method provided by
# `accepts_nested_attributes_for :choices`
def choices=(value)
value.is_a?(Array) && value.first.is_a?(Hash) ? (self.choices_attributes = value) : super
end
I am trying to include a virtual attribute/method within a respond_to JSON hash.
The Model (employee.rb)
attr_reader :my_method
def my_method
return "foobar"
end
The Controller (employees_controller.rb)
respond_to :json
def index
#employees = Employee.all
respond_with(:data => #employees, :total => Employee.all.count)
end
It is important that I have "data" as the json root for the collection of "employees" and also to include the "total" within the hash. This works well and returns a nice JSON result of all the employees and the total value.
My qustion is: How do I include the virtual attribute "my_method" for each employee within the employees hash in the JSON response?
Thanks for your time!
This is what worked for me.
Employee.rb
def as_json(options={})
super.as_json(options).merge({:my_method => my_method})
end
Thanks for cmason for pointing me in the right direction. Any other solutions are welcome.
In Rails 3 one can use following
#yourmodel.to_json(methods: ['virtual_attr1', 'virtual_attr2']
Overwriting as_json in your model should do the trick:
def as_json(options={})
{ :methods=>[:my_method] }.merge(options)
end
My setup: Rails 2.3.10, Ruby 1.8.7
I have experimented, without success, with trying to access a virtual attribute in a model from a JSON call. Let's say I have the following models and controller code
class Product
name,
description,
price,
attr_accessor :discounted_price
end
class Price
discount
end
class ProductsController
def show
#product = Product.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #product }
end
end
end
What I like is to have the JSON output also include Product.discounted_price which is calculated in real-time for each call, ie discounted_price = Price.discount * Product.price. Is there a way to accomplish this?
SOLUTION:
With the initial help from dmarkow, I figured it out, my actual scenario is more complex than the above example. I can do something like this, in the Product model, add a getter method
def discounted_price
...# do the calculation here
end
In the JSON call do this
store = Store.find(1)
store.as_json(:include => :products, :methods => :discounted_price)
You can run to_json with a :methods parameter to include the result of those method(s).
render :json => #product.to_json(:methods => :discounted_price)
Have a look at the gem RABL, as shown in this railscast:
http://railscasts.com/episodes/322-rabl?view=asciicast
RABL gives you fine grained control of the json you produce, including collections and children.