Rails 5 - include nested associations not working - ruby-on-rails

I have two simple models:
note.rb
with
:title -> string, :content -> string
has_and_belongs_to_many :tags, join_table: :tags_notes
accepts_nested_attributes_for :tags
tag.rb
with
:name -> string
has_and_belongs_to_many :notes, join_table: :tags_notes
Both models are connected through has_and_belongs_to_many relationship.
The association table is called tags_notes as indicated above.
Well, the problem I have here is, in my RESTful controller, to get notes, I have this:
GET /api/notes
This only returns Note objects:
[
{
"id": 1,
"title": "12231",
"content": "121213"
},
{
"id": 2,
"title": "test",
"content": "testtest"
}
]
However, each note has tags, and I would like to dump those in the response as well, like this:
[
{
"id": 1,
"title": "12231",
"content": "121213",
tags: [
{
"name": "test",
"id": 1
},
{
...
}
]
},
...
]
In my controller, I've tried
Note.includes(:tags).
Current controller code:
def index
notes = Note.includes(:tags)
render json: notes, status: :ok
end
They only seem to return notes, without tags. Same is the case with Note.eager_load(:tags) What am I doing wrong? Cannot find enough documentation that will help me fix this issue.
If someone can help me with this I will be grateful.
Thanks a bunch.

Shortly after posting my question I found the answer myself. The include has to go in render.
So the controller code
def index
notes = Note.all
render json: notes, :include => :tags, status: :ok
end
Seems to do the trick!

Related

Rails Active model serializer returns array instead of json

I am using Rails to create APIs containing basic todo information: name, list, and items. I want it to return json format, to look something like this:
{
"data": [
{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API is awesome!",
"body": "You should be using JSON API",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
}
}
],
"links": {
"href": "http://example.com/api/posts",
"meta": {
"count": 10
}
}
}
^code from Active Serializer's Github.
When I look on my localhost http://localhost:3000/api/users/, it shows
[{"id":1,"username":"Iggy1","items":[{"id":1,"list_id":1,"name":"Wash dishes","completed":true},{"id":7,"list_id":1,"name":"Finish this assignment","completed":false}],"lists":[{"id":1,"name":"Important!","user_id":1,"permission":"private"},...
It is returning an array of hashes. I am sure I missed an important step when I was setting up my serializer. How can I reformat my array of hashes into JSON API format?
I've read getting started guide, rendering, and JSON API section but I still couldn't figure it out. I might have overlooked it.
Some of my codes:
app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :username#, :email
has_many :items, through: :lists
has_many :lists
end
app/controllers/api/users_controller.rb
def index
#users = User.all
render json: #users, each_serializer: UserSerializer
end
routes
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
resources :users do
resources :lists
end
end
end
Let me know if I can clarify it better. Thanks!!
(Answer from comments)
To use the JSON API adapter, you need to declare you want to use it.
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
As per the AMS readme.
ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
from 0-10-stable documentation
However, this did not solve my problem. It was due to the Hash being nested as discussed in this SO post

Add method to json output with include in rails

I am getting info from my database
#teams = Team.select(:id,:name)
Then, I am rendering this info with an assosiation
render json: #teams, include: :players
This produce me the following input:
[
{
"id": 1,
"name": "Lyon Gaming",
"players": [
{
"name": "Jirall",
"position": "Superior",
},
{
"name": "Oddie",
"position": "Jungla",
}
]
}
]
How ever I need to sort the players in an specific order (not alphabetical)
I already have the sql statement to do it; how ever I dont know how to apply this method to each association.
I am looking something like:
render json: #teams, (include: :players, method: custom_order)
But this raise me an error.
Any ideas?
This should work:
render json: #teams, include: :players, methods: :custom_order
I'd like to add that if you will always want to return the object this way, you can add this to your model instead:
def as_json(options = nil)
super(include: :players, methods: :custom_order)
end
As Adam has mentioned, active_model_serializers is good to have if your render json's start getting ugly. If the complexity doesn't grow too much, I always recommend keeping the dependencies down.
https://github.com/rails-api/active_model_serializers
You need to read more about serializers in rails(This is good practice)
Here you have a nice article about this https://www.sitepoint.com/active-model-serializers-rails-and-json-oh-my/
Then you can make something like this
app/serializers/team_serializer.rb
class TeamSerializer < ActiveModel::Serializer
attributes :id, :name, :ordered_players
end
app/controllers/teams_controller.rb
def index
respond_with(Team.all.map(&TeamSerializer.method(:new)))
end

How to parse POST request in JSON format in rails

I'm trying to send a POST request in JSON via curl in ruby on rails. I would like to know how can I parse this request in a controller and save it in database. I've created controller and model for a player.
Below is the curl command -
curl -i -X POST -H "Content-Type: application/json" "localhost:3000/app/v2/player" -d '{
"url": "app/v2/players.json",
"para": {
"player": {
"player_id": 51275,
"email": "test#gmail.com",
"total_won": "1890000.00",
"pr_costs": "29.00",
"payment_method": "VISA",
"games": [
{
"name": "AvsB",
"matches": 1,
"value": 1100,
"category": "Cricket",
"subcategory": "Test",
"tags": [
"Aus",
"Melbourne"
],
"game_id": 12
},
{
"name": "CvsD",
"matches": 1,
"value": 790,
"category": "Cricket",
"subcategory": "T20",
"tags": [
"T20",
"20over",
"worldcup"
],
"game_id": 7
}
]
}
}
}'
Below is my player controller-
module App
module V2
class PlayersController < ApplicationController
def create
#player = Player.new(params[[:para][:player][:player_id]].first)
if #player.save
render json: #player, status: 201, location: #player
else
render json: #player.errors, status: 422
end
end
private
def player_params
#params.require(:player).permit(:url, :player_id, :email, :total_won, :pr_costs, :payment_method)
params.fetch(:url,:para, {}).permit(:player_id, :email, :total_won, :pr_costs, :payment_method)
end
end
end
end
Below is my player model -
class Player < ActiveRecord::Base
has_many :games, dependent: :destroy
validates :player_id, presence: true
attr_accessor :url,:para
end
Currently I'm getting below error while initiating the curl command -
TypeError (no implicit conversion of Symbol into Integer):
app/controllers/app/v2/players_controller.rb:21:in `[]'
app/controllers/app/v2/players_controller.rb:21:in `create'
Any pointers on how I can read JSON data and create records for players in create function and related games would be appreciated. I've not created the model for games but it'll be similar in structure to JSON data. Also one player has many games and a game will belong to a Player.
Thanks in advance!
This is badly structured.
[[:para][:player][:player_id]]
It basically means (in part) there's an array that contains the player symbole
[:player]
and you want to get the ':player_id(th)' entry of that array or string... which is where you're getting the error ("How do I convert a symbol into an integer for the array?")
[:player][:player_id]
you probably want...
params[:para][:player][:player_id]
There's no 'first' because there's no array in the :player_id entry.
As it always happens, I figured it out myself. Below is what I used for player_params function. There could be other ways as well, but this certainly worked for me.
params.require(:player).permit(:url,:para =>[:player =>[:player_id, :email, :total_won, :pr_costs, :payment_method,:games =>[:name, :matches, :value, :category, :subcategory, :game_id ]]])
#SteveTurczyn thanks for pointing me in the right direction

Rails JBuilder recursive lookup

I am using Rails 4, and I have some simple models as follow:
class Question < ActiveRecord::Base
# columns (id, text)
has_many :answers
end
class Answer < ActiveRecord::Base
# columns (id, text, next_question_id)
belongs_to :question
end
You can see that an answer has a next_question_id column, which will be used to look up another question. I want to generate a tree-structure json like this:
{
"text": "This is question 1",
"answers": [
{
"text": "This is answer a",
"next_question": {
"text": "This is question 2",
"answers": [
{
"text": "This is answer c",
"next_question":
}
]
}
},
{
"text": "This is answer b",
"next_question": {
"text": "This is question 2",
"answers": [
{
"text": "This is answer d",
"next_question":
}
]
}
}
]
}
How can I achieve this with JBuilder? I tried the solution here, but I cannot pass the json argument to the helper function.
The standard aproach for rendering trees is using a recursive partial. To implement this you'll first need to add a method to your Answer model like this.
def next_question
Question.find(next_question_id) if next_question_id
end
(hint: alternetively you could just set a belongs_to :next_question, class_name: Question association on your Answer model)
Then you create a partial like _question.json.jbuilder that goes like this:
json.(question,:id, :text)
json.answers question.answers do |answer|
json.(answer, :id, :text)
json.partial!(:question, question: answer.next_question) if answer.next_question
end
Then in the controller you take the first question in your survey and put it in say the #first_question variable.
And the last thing: in your view you write
json.partial! :question, question: #first_question

Rails 3: Wrapping as_json response with additional lines

I was very happy to learn of as_json to make my code DRY. And I've added the following to a model:
class ProductType < ActiveRecord::Base
has_many :component_types
def as_json(parameter)
{:name => self.name,
:description => self.description,
:children => self.componentTypes}
end
end
This is great. The only thing is that for my client side application, I need to wrap the response I get into this format, (where "items" contains what is created by as_json):
{
"identifier": "name",
"label": "name",
"items":
[
{
"name": "myName1",
"description": "myDesc1",
"children":[]
},
{
"name": "myName2",
"description": "myDesc2",
"children":[]
}
]
}
There are a lot of limitations to overriding as_json, and your issue is one of them. I'd suggest having a look at the RABL gem as I think it will help you reach your goal.

Resources