I'm working on an API based Rails application, I don't know how to show all associated items in one request like my models
todo.rb
class Todo < ApplicationRecord
has_many :items, dependent: :destroy
validates_presence_of :title, :created_by
end
item.rb
class Item < ApplicationRecord
belongs_to :todo
validates_presence_of :name
end
in the controller
def show
json_response(#todo)
end
private
def set_todo
#todo = Todo.find(params[:id])
end
The endpoit for single todo
https://example.com/todos/1
and it showing like this
{
"id": 1,
"title": "Hello World!",
"created_by": "2",
"created_at": "2018-04-26T11:19:31.433Z",
"updated_at": "2018-04-26T11:19:31.433Z"
}
My question is how do I show all items which created by this todo in same end point request.
Try using ActiveModel::Serializers::JSON#as_json
For e.g.
def show
render json: #user.as_json(include: :posts), status: 200
end
That should return a response like
{
"id": 1,
"name": "Konata Izumi",
"age": 16,
"created_at": "2006/08/01",
"awesome": true,
"posts": [
{
"id": 1,
"author_id": 1,
"title": "Welcome to the weblog"
},
{
"id": 2,
"author_id": 1,
"title": "So I was thinking"
}
]
}
def show
render json: {"todos" => {"todo" => #todo, "items" => #todo.items }}, status:200
end
Alternatively you can use serializers for more filters and modifications of response datas
Related
I want this form
"data": [
{
"id": 2,
"searchable_type": "User",
"email": "abc",
"first_name": "abc",
"last_name": "xyz",
"created_at": "2022-08-05T09:40:18.986Z",
"updated_at": "2022-08-05T09:40:18.986Z"
},
{
"id": 3,
"searchable_type": "blog",
"tittle": "user",
"created_at": "2022-08-05T09:40:18.986Z",
"updated_at": "2022-08-05T09:40:18.986Z"
}
]
I want to return whole object for each model respectively by using Pg Search multi search it just return a content ,searchable_type and searchable_id as attached images belowIt is my postman image for blog returnIt is my postman image for user return
[My search controller, User and blog models]
class Api::V1::SearchController < Api::V1::ApiController
def index
#query = params[:value]
#results = PgSearch.multisearch(#query)
render json: { data: #results}, status: :ok
end
end
class Blog < ApplicationRecord
include PgSearch::Model
multisearchable against: [:id, :title, :created_at, :updated_at ]
end
class User < ApplicationRecord
include PgSearch::Model
multisearchable against: [:id, :email, :first_name, :last_name ]
end
Change
#results = PgSearch.multisearch(#query)
to
#results = PgSearch.multisearch(#query).includes(:searchable).map(&:searchable)
I have a Component class that can be either a Section or Question. A Section can have many Component (i.e. Question).
I'm trying to get the serializer to send back the Component(Section) and its associated Component(Question). Right now, based on my code below, I'm getting back the Question and the Section object that it's associated to (see below). How can I get the serializer to return what I'm expecting?
Response (current):
{"data":
[{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null,
"section": null
},
{"id": 2,
"type": "Question",
"content": "Q1",
"section_id": 1,
"section":
{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null
}
},
{"id": 3,
"type": "Question",
"content": "Q2",
"section_id": 1,
"section":
{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null
}
}]
}
Response (expected):
{"data":
[{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null,
"section":
{"id": 2,
"type": "Question",
"content": "Q1",
"section_id": 1
},
{"id": 3,
"type": "Question",
"content": "Q2",
"section_id": 1
}
}]
}
ComponentSerializer:
class ComponentSerializer < ActiveModel::Serializer
belongs_to :section
attributes :id,
:type,
:content,
:section_id
end
SectionSerializer:
class ComponentSerializer < ActiveModel::Serializer
has_many :components
attributes :id,
:type,
:content,
:section_id
end
component.rb (model):
class Component < ApplicationRecord
# == Associations ==========================
belongs_to :project
belongs_to :section,
class_name: 'Section',
foreign_key: :section_id,
optional: true
end
section.rb (model):
class Section < Component
# == Associations ==========================
has_many :components,
class_name: 'Component',
foreign_key: :component_id,
dependent: :destroy
end
component_controller.rb:
before_action :load_project
before_action :load_scope
def index
components = paginate #scope, per_page: per_page
data = ActiveModelSerializers::SerializableResource.new(
components,
each_serializer: ComponentSerializer
)
render_response(:ok, { data: data })
rescue ActiveRecord::RecordNotFound
render_response(:not_found, { resource: "Project/Lesson" })
end
def load_scope
#scope = #project.components
#scope = #scope.order("position")
end
def load_project
#project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_response(:not_found, { resource: "Project/Lesson" })
end
Hey so I'm new to active record I have these two map functions here and I'm wondering if there's a better/more performant way to make this below query:
Right now I'm getting hundreds of lines in my terminal when I execute the query and I'm afraid it could take a lot of memory if a lot of people are loading these converations at the same time?
Step 1 is getting all the relevant conversations
Step 2 is making a conversation hash with all information regarding the sender, recipient and messages linked to that conversation.
Step 3 is getting all these conversation sorted in the right order which is to sort them from the conversation with the most recent message at the top.
EDIT 1: Rails version 5.0.1
def index
i = 1
#messages = []
#user = User.find_by(email: params[:email])
#message = Conversation.where("sender_id = ? OR recipient_id = ?", #user.id, #user.id)
#message.map { |conversation|
#messages << conversation if Message.where(conversation_id: conversation.id).count > 0
}
render json: #messages.map { |conversation|
{
date: conversation.messages.last.created_at,
sender: User.find(conversation.sender_id),
recipient: User.find(conversation.recipient_id),
conversation: {
id: conversation.id,
messages: Message.where(conversation_id: conversation.id).sort_by{|e| e[:created_at]}.reverse.map {|message| {
sender: {
email: User.find(message.user_id).email,
first_name: User.find(message.user_id).first_name,
last_name: User.find(message.user_id).last_nam },
body: message.body,
created_at: message.created_at
}
}
}}
}.sort_by { |hsh| hsh[:date] }.reverse
end
class Message < ApplicationRecord
belongs_to :conversation, touch: true
end
class Conversation < ApplicationRecord
has_many :messages, -> { order(created_at: :desc) }
belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
end
class User < ApplicationRecord
has_many :conversations, -> (user) { unscope(where: :user_id).where('conversations.sender_id = ? OR conversations.recipient_id = ?', user.id, user.id) }
end
class ConversationsController < ApplicationController
def index
#user = User.find_by(email: params[:email])
# "joins(:messages)" allows only retrieving conversations having at least one message, and does not include conversation with 0 message
# now ordering by `updated_at: :asc` because `Message belongs_to :conversation, touch: true`, in which Conversation's updated_at will be automatically "touched"/updated whenever the associated Messages are updated/created.
#conversations = #user.conversations.joins(:messages).order(updated_at: :asc).distinct
json_response = #conversations.as_json(
only: [:id, :updated_at],
include: {
sender: {
only: [:id, :first_name, :last_name, :email]
},
recipient: {
only: [:id, :first_name, :last_name, :email]
},
messages: {
only: [:body, :created_at]
}
}
)
render json: json_response
end
end
Example Request
Started GET "/conversations?email=foobar#example.com"
Example Response
[{
"id": 2,
"updated_at": "2018-02-02T11:17:45.376Z",
"sender": {
"id": 4,
"first_name": "Lorem",
"last_name": "Ipsum",
"email": "loremipsum#example.com"
},
"recipient": {
"id": 1,
"first_name": "Foo",
"last_name": "Bar",
"email": "foobar#example.com"
},
"messages": [{
"body": "Hello there",
"created_at": "2018-02-02T11:17:45.367Z"
}, {
"body": "Whatcha doin'?",
"created_at": "2018-02-02T11:17:36.451Z"
}, {
"body": "hahaha :)",
"created_at": "2018-02-02T11:03:29.843Z"
}]
}, {
"id": 1,
"updated_at": "2018-02-02T11:36:14.275Z",
"sender": {
"id": 1,
"first_name": "Foo",
"last_name": "Bar",
"email": "foobar#example.com"
},
"recipient": {
"id": 5,
"first_name": "Ruby",
"last_name": "Rails",
"email": "rubyonrails#example.com"
},
"messages": [{
"body": "hello Ruby-on-Rails! :)",
"created_at": "2018-02-02T11:36:14.267Z"
}]
}]
You can check out how to use .as_json here
You can check out how to use touch: true here
Tested working
I am using AMS version 0.10 and looking to use the json-api specification for rendering my responses. However, I am having difficultly rendering the 'included' key for my relationship data. I have the following setup:
products_controller.rb
class Api::V1::ProductsController < ApplicationController
...
respond_to :json
def show
respond_with Product.find(params[:id])
end
...
product_serializer.rb
class ProductSerializer < ActiveModel::Serializer
attributes :id, :title, :price, :published
has_one :user
end
user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :auth_token, :created_at, :updated_at
end
products_controller_spec.rb
before(:each) do
#product = FactoryGirl.create :product
get :show, params: { id: #product.id }
end
...
it "has the user as a embeded object" do
product_response = json_response
puts "&&&&&&&&&&&&&&&"
puts product_response #output below
#expect(product_response[:user][:email]).to eql #product.user.email
end
...
json_response
{:data=>{:id=>"1", :type=>"products", :attributes=>{..working..}, :relationships=>{:user=>{:data=>{:id=>"1", :type=>"users"}}}}}
I would like to know how to get the 'included' section for the nested resource.
Example (from http://jsonapi.org/format/#introduction)
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
},
I have never used AMS before so any help will be greatly appreciated.
Many thanks
Just for anyone else the solution is here https://github.com/rails-api/active_model_serializers/blob/master/docs/jsonapi/schema.md.
Essentially i add the following to my controller action (GET products/1)
render json: product, include: params[:include]
This will allow the requesting system to determine whether they would like to include the nested models by adding the parameter include='user' for the api to process.
Thanks
I'm trying to write an update method that processes JSON. The JSON looks like this:
{
"organization": {
"id": 1,
"nodes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
Organization model:
has_many :nodes
accepts_nested_attributes_for :nodes, reject_if: :new_record?
Organization serializer:
attributes :id
has_many :nodes
Node serializer:
attributes :id, :title, :description
Update method in the organizations controller:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_attributes: node_params.except(:id))
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(nodes: [:id, :title, :description])
end
I also tried adding accepts_nested_attributes_for to the organization serializer, but that does not seem to be correct as it generated an error (undefined method 'accepts_nested_attributes_for'), so I've only added accepts_nested_attributes_for to the model and not to the serializer.
The code above generates the error below, referring to the update_attributes line in the update method. What am I doing wrong?
no implicit conversion of String into Integer
In debugger node_params returns:
Unpermitted parameters: id
{"nodes"=>[{"id"=>101, "title"=>"gsdgdsfgsdg.", "description"=>"dgdsfgd."}, {"id"=>1, "title"=>"ertret.", "description"=>"etewtete."}]}
Update: Got it to work using the following:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_params)
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(:id, nodes_attributes: [:id, :title, :description])
end
To the serializer I added root: :nodes_attributes.
It now all works, but I'm concerned about including the id in node_params. Is that safe? Wouldn't it now be possible to edit the id of the organization and node (which shouldn't be allowed)? Would the following be a proper solution to not allowing it to update the id's:
if organization.update_attributes(nodes_params.except(:id, nodes_attributes: [:id]))
looks super close.
Your json child object 'nodes' need to be 'nodes_attributes'.
{
"organization": {
"id": 1,
"nodes_attributes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
You can do this sort of thing. Put this in your controller.
before_action do
if params[:organization]
params[:organization][:nodes_attributes] ||= params[:organization].delete :nodes
end
end
It will set the correct attribute in params and still use all the accepts_nested_attributes features.