Include properties for ActiveModelSerializer only if called within has many - ruby-on-rails

I have a rails app with the following models.
class Project
has_many :project_clips
has_many :clips, through: :project_clips
end
class Clip
has_many :project_clips
has_many :projects, through: :project_clips.
end
class ProjectSerializer < ActiveModel::Serializer
attributes :id, :name
has_many :clips
end
class ClipSerializer < ActiveModel::Serializer
attributes :id, :name
end
I was wondering if it's possible to display the values of the associated project_clip, if the clip has been called within the context of project.
Let's say the ProjectClip model, has a field called priority. I want the results to show up like this.
{ projects: { "id": 1, "name": "ipsum", "clips": [{ "id": 1, "name": "lorem", "priority": "high"}] } }
I don't want the values of project_clips to be included, just a few properties when returning the data for projects.

If I'm getting your question right, you can do something like:
res = project.clips.collect{ |clip| [clip, clip.project_clips] }
or if you want to return hashes and not objects, you can do:
res = project.clips.collect{ |clip| [clip.attributes, clip.project_clips.collect{|pc| pc.attributes}] }

Related

Rails: include nested through association

I have a Store model, which has an id and other fields.
I have a Product model which has an id and other field
To join them I use Amount model. Here are the models:
class Product < ApplicationRecord
has_many :amounts
has_many :stores, through: :amounts
end
class Store < ApplicationRecord
has_many :amounts
has_many :products , through: :amounts
end
class Amount < ApplicationRecord
belongs_to :store
belongs_to :product
end
Amount has in_stock property. I want my json to look like this:
{stores: [
{"id": 1,
"name": "New Store",
"products": ["id": 1, "name": "Toothbrush", "in_stock": 4000]}
]}
I tried render json: {stores: stores}.to_json(include: :products), I also tried a nested include but it didn't work as well, it showed output for all amounts
Based on your comments on my original answer (which shows in stock comes from the Amount model) you can include amounts in the includes relation which would be a cleaner solution and more performant...
output = Store.all.includes(amounts: :product).map do |store|
{ id: store.id, name: store.name,
products: store.amounts.map do |amount|
{ id: amount.product.id, name: amount.product.name, in_stock: amount._in_stock }
end
}
end.to_json
output = Store.all.includes(:products).map do |store|
{ id: store.id, name: store.name,
products: store.products.map do |product|
{ id: product.id, name: product.name, in_stock: product.in_stock }
end
}
end.to_json
This builds an array of store hashes, each store hash has an array of products, and the hash is converted to json.

Use alternate association id for ActiveModel::Serializer association

I have an app where there I have normal ActiveRecord ids as well as a unique field (e.g. ident) that's unique on an external, canonical database. A model looks like:
class Parent
has_many :childs, foreign_key: :parent_ident, primary_key: :ident
end
class Child
belongs_to :parent, foreign_key: :parent_ident, primary_key: :ident
end
For various reasons I'd like the consumer of my Rails API to use the canonical ids (e.g. ident) not the ids defined on the app. So I've defined my serializers (using ActiveModel::Serializer):
class ParentSerializer < ActiveModel::Serializer
attributes :id, :ident, :other, :stuff
has_many :children
def id
object.ident
end
end
class ChildSerializer < ActiveModel::Serializer
attributes :id, ident, :parent_ident, :things
def id
object.ident
end
end
the problem is that the JSON generated correctly is using my overridden IDs for the top-level attributes but the IDs in the child_ids field are the local ids not the canonical idents I want to use.
{
parents: [
{
id: 1234, // overridden correctly in AM::S
ident: 1234,
other: 'other',
stuff: 'stuff',
child_ids: [ 1, 2, 3 ], // unfortunately using local ids
}
],
childs: [
{
id: 2345, // doesn't match child_ids array
ident: 2345,
parent_ident: 1234,
things: 'things'
}
]
}
Question: is there a way to make the parent serializer use the ident field of it's association rather than the default id field?
I have tried putting a def child_ids in the ParentSerializer without success.
I am using Rails 4.2 and the 9.3 version of active_model_serializers gem.
You can specify custom serializers for associations as per the docs
has_many :children, serializer: ChildSerializer

Serialize a summary of a has_many relationship

How can I include a summary of the associated objects rather than the objects itself. For example, if a client has_many projects I could do this:
class ClientSerializer < ActiveModel::Serializer
attributes :id, :name
has_many :projects
end
But this will return all of the associated projects. I would much rather bring back just a count of the projects, the url to download the full list of projects, the last time a project was updated, etc.
What is the best way to include a summary of the associated objects?
Ideally, for example the resulting JSON would look like this:
{
"id": 10,
"name": "My Client",
"projects": {
"count": 5,
"updated_at": "2014-09-09T13:36:20.000-04:00",
"url": "https://my.baseurl.com/clients/10/projects"
}
I'm not sure if this is the best way to do it, but I got this to work:
class ClientSerializer < ActiveModel::Serializer
attributes :id, :name, :archive, :updated_at, :projects
def projects
collection = object.projects.to_a
{ count: collection.length,
updated_at: collection.map(&:updated_at).max,
url: projects_url }
end
end
You could create an instance method:
class ClientSerializer < ActiveModel::Serializer
has_many :projects
def project_count
projects.size
end
end

Rails Active Model Serializer - has_many and accessing the parent record

I'm trying to build a JSON representation of some Rails models using Active Model Serializer, where some models embed others. For example, I have Event and Attendees, Event has_and_belongs_to_many Attendees.
class EventSerializer < ActiveModel::Serializer
attributes :name
has_many :attendees, serializer: AttendeeSerializer
end
class AttendeeSerializer < ActiveModel::Serializer
attributes :name
end
This would result in JSON like { name: 'Event One', attendees: [{ name: 'Alice' }, { name: 'Bob' }] }.
Now, I'd like to add what the attendees have said about the event. Let's say, Comment belongs_to Event, belongs_to Attendee. I'd like to include said comments in the serialized output of event, so it would become { name: 'Event One', attendees: [{ name: 'Alice', comments: [{ text: 'Event One was great!'}] }, { name: 'Bob', comments: [] }] }.
I could have
class AttendeeSerializer < ActiveModel::Serializer
attributes :name
has_many :comments
end
but that would select all the comments by this attendee for all the events - not what I want. I'd like to write this, but how do I find the particular event for which I'm doing serialization? Can I somehow access the 'parent' object, or maybe pass options to a has_many serializer?
class AttendeeSerializer < ActiveModel::Serializer
attributes :name
has_many :comments
def comments
object.comments.where(event_id: the_event_in_this_context.id)
end
end
Is this something that can be done, or should I just build the JSON in another way for this particular use case?
I'd do things manually to get control:
class EventSerializer < ActiveModel::Serializer
attributes :name, :attendees
def attendees
object.attendees.map do |attendee|
AttendeeSerializer.new(attendee, scope: scope, root: false, event: object)
end
end
end
class AttendeeSerializer < ActiveModel::Serializer
attributes :name, :comments
def comments
object.comments.where(event_id: #options[:event].id).map do |comment|
CommentSerializer.new(comment, scope: scope, root: false)
end
end
end

How can I store nested json data in Rails?

I have 3 models:
class Depot < ActiveRecord::Base
has_many :car_amounts
has_many :cars, :through => :car_amounts
end
class CarAmount < ActiveRecord::Base
belongs_to :depot
belongs_to :car
end
class Car < ActiveRecord::Base
has_many :car_amounts
has_many :depots, :through => :car_amounts
end
What is the best way to store json paramers, which contains depot, amounts and cars data. Something like this:
{
"Depots": {
"title": "Dealer1"
},
"Amounts": [
{
"amount": "1"
"car": [
{
"Type": "supercar1"
}
]
},
{
"amount": "5"
"car": [
{
"Type": "supercar2"
}
]
},
]
}
I am a little unclear what your question is, but I think you might be looking for accepts_nested_attributes_for. The documentation can be found here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html. From the documentation:
Nested attributes allow you to save attributes on associated records through the parent.
As an example, you could add accepts_nested_attributes_for :cars in you Depots model. While it wouldn't quite work with the JSON you've shown above, it works well with the general idea.
If this isn't quite your question then please comment and I will clarify.
If this data needs to be in you your database then I would 'seed' the data and import it into your database. You can create a file db/seed.rb and lets say I want to import students into my database. I have a student model already created/migrated. In my seed.rb I would do something like...
students = ActiveSupport::JSON.decode(File.read('db/students.json'))
students.each do |a|
Student.find_or_create_by_id_number(:id_number => a['id_number'], :username => a['username'], :first_name => a['first_name'], :last_name => a['last_name'])
end
...and my students.json looks like...
[
{"id_number": "00xx280","username": "jesxxne","first_name": "Jessie","last_name": "Lane","email": "jesxxane#xx.edu","phone_number": "602-321-6693","is_active": true ,"last_four": "1944" },
{"id_number": "11xx2","username": "jamxxacqua","first_name": "James","last_name": "Bevixxua","email": "jamesbxxacqua#xx.edu","phone_number": "828-400-5393","is_active": true ,"last_four": "5422" }
]
Now you can run rake db:seed to import this data. Good luck!

Resources