Rails 4 + serialization: Nested models not showing - ruby-on-rails

I am using the Active Model Serialization Gem to pull info for my API and my nested models are not working properly.
I have the following:
class API::ClientSerializer < ActiveModel::Serializer
attributes :name, :address
has_many :check_ins
end
class API::CheckInSerializer < ActiveModel::Serializer
attributes :id, :employee, :created_at, :type_of_weighin, :user_id
has_one :weigh_in
end
and while the check_ins show when I pull a client. The weigh_in is never included in the check_in. Is there something else I need to do?

Related

How to pass attributes in Serializer not in model in Rails (ActiveRecord)?

I am building a Rails backend that has Users, Sightings and Comments. The modelComment joins Sighting and User. I would like to pass the user's name or username along with the user_id which is an attribute of join table Comments in CommentSerializer to my front-end.
I can access this data using Active Record or Ruby methods but how do actually make this an attribute to be passed through Serializer?
Here's my CommentSerializer file:
class CommentSerializer < ActiveModel::Serializer
attributes :id, :body, :likes, :user_id
belongs_to :commentable, :polymorphic => true
belongs_to :sighting
has_many :comments, as: :commentable
end
add to your attributes :user_name for example and then add
def user_name
object.user.name
end
in your CommentSerializer class
So I had good luck with this method in my Serializer file to pass down this attribute:
def user_name
User.all.find { |f| f.id == object.user_id }.username
end

Active Model Serializer use no serializer

At times I'd like to use no serializer for a model, and other times I do. I have tried to request nil serializer, but it seems that a serializer is used regardless.
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :id, :name, :weeks_remaining
has_many :check_ins, serializer: nil
end
In this instance I'd like to return the association without any serializer, but it still uses the CheckInSerializer anyway.
Is there a way around this?
I think you could just do:
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :check_ins, :id, :name, :weeks_remaining
end
Or if that doesn't work:
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :check_ins, :id, :name, :weeks_remaining
def check_ins
object.check_ins.map(&:as_json)
end
end

multi-tenant and use of ruby delegate vs NoSQL for tenant context

I have a multi-tenant app (rails backend) and I need to mix a tenant-agnostic pre-populated list of items with tenant specific attributes.
Was wondering if I anyone has used delegate to get this done and had ok performance vs. venturing out of Postgres into MongoDB
example models:
Class EquipmentList < ActiveRecord::Base
attr_accessible :name, :category_id
has_one :tenant_equipment_list
delegate :alt_name, to: tenant_equipment_list
end
Class TenantEquipmentList < ActiveRecord::Base
attr_accessible :alt_name, :group, :sku, :est_price, :equipment_list_id
belongs_to :equipment_list
default_scope { where(tenant_id: Tenant.current_id) }
end
I've run this successfully on a low traffic site. The rails back end handles this pretty well in outputting to json.
Realized its better to use the tenant version as the foreign key and delegate master attributes to tenant. Looks something like this:
Class EquipmentList < ActiveRecord::Base
attr_accessible :name, :category_id
has_one :tenant_equipment_list
end
Class TenantEquipmentList < ActiveRecord::Base
attr_accessible :alt_name, :group, :sku, :est_price, :equipment_list_id
belongs_to :equipment_list
delegate :name, to: tenant_equipment_list #prefix: true if you want
default_scope { where(tenant_id: Tenant.current_id) }
end

Serializing a custom attribute

I am using the Active Model Serializer gem for my application. Now I have this situation where a user can have an avatar which is just the ID of a medium.
I have the avatar info saved into Redis. So currently what I do to show the avatar at all in the serialized JSON, is this:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
Medium.find(self.current_avatar)[0]
end
#has_one :avatar, serializer: AvatarSerializer
has_many :media, :comments
url :user
end
I query Redis to know what medium to look for in the database, and then use the result in the :avatar key.
Down in the code there is also a line commented out, that was the only way I found on the https://github.com/rails-api/active_model_serializers/ page about using a custom serializer for something inside of serializer.
So to get to my problem. Right now the :avatar comes just like it is in the database but I want it to be serialized before I serve it as JSON.
How can I do that in this case?
You need to serialize Avatar Class:
class Avatar
def active_model_serializer
AvatarSerializer
end
end
Then you just use this way:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
# Strange you query another object
avatar = Medium.find(object.current_avatar).first
avatar.active_model_serializer.new(avatar, {}).to_json
end
has_many :media, :comments
url :user
end
According to the docs if you want a custom serializer you can just add:
render json: avatar, serializer: AvatarSerializer
or whatever your serializer name could be, here are the docs:
https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/serializers.md#scope

How to serialize nested Model in Rails?

I'm having an extremely difficult time figuring out how to serialize the nested attributes of a model in rails. I have a RecipeTemplate which will store an already existing Recipe in it's template_data attribute. Recipe has nested attributes two levels deep.
This is on rails 3.1.0.rc4
class RecipeTemplate < ActiveRecord::Base
serialize :template_data, Recipe
...
end
class Recipe < ActiveRecord::Base
has_many :ingredients
accepts_nested_attributes_for :ingredients
...
end
Ingredients in Recipe also has nested attributes (SubIngredients).
If I set the template_data with an object like so:
Recipe.includes(:ingredients => [:sub_ingredients]).find(1)
I'll get a TypeError "can't dump anonymous class Class" which makes sense, since it doesn't know how to serialize the Ingredients or SubIngredients.
How can you serialize the nested attributes in a model so that you can use:
serialize :template_data, Recipe
Or do I have to serialize the data in some other manner and perform the type safety checks myself?
Thanks in advance for any help
I can see why you would want the template itself to be stored inside of a serialized column, but you need a little more manipulation of the data being stored than is permitted by that type of column. Here's what I would do:
app/models/recipe_template.rb
class RecipeTemplate < ActiveRecord::Base
serialize :template_data
attr_accessible :name, :recipe
def recipe=(r)
self.template_data = r.serializable_hash_for_template
end
def recipe
Recipe.new(template_data)
end
end
app/models/recipe.rb
class Recipe < ActiveRecord::Base
has_many :ingredients, as: :parent
accepts_nested_attributes_for :ingredients
attr_accessible :name, :ingredients_attributes
def serializable_hash_for_template(options={})
options[:except] ||= [:id, :created_at, :updated_at]
serializable_hash(options).tap do |h|
h[:ingredients_attributes] = ingredients.map(&:serializable_hash_for_template)
end
end
end
app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :parent, polymorphic: true
has_many :sub_ingredients, class_name: 'Ingredient', as: :parent
accepts_nested_attributes_for :sub_ingredients
attr_accessible :name, :sub_ingredients_attributes
def serializable_hash_for_template(options={})
options[:except] ||= [:id, :parent_id, :parent_type, :created_at, :updated_at]
serializable_hash(options).tap do |h|
h[:sub_ingredients_attributes] = sub_ingredients.map(&:serializable_hash_for_template)
end
end
end
Then to create and use a template:
# create a recipe to use as a template
taco_meat = Ingredient.create(name: "Taco Meat")
taco_seasoning = taco_meat.sub_ingredients.create(name: "Taco Seasoning")
sams_tacos = Recipe.create(name: "Sam's Tacos")
sams_tacos.ingredients << taco_meat
# create a template from the recipe
taco_recipe = RecipeTemplate.create(name: "Taco Recipe", recipe: sams_tacos)
# build a new recipe from the template
another_taco_recipe = taco_recipe.recipe
The difference is that you're using the serialized column to store a Hash to use in the Recipe contructor. If you just wanted to serialize the object, the other posters are correct–just associate an object.
Why don't you just keep a field in your RecipeTemplate model with the label recipe_id
and link that to the recipe instead of trying to serialize a recipe object?

Resources