Rails Active Model Serializer not showing correct attributes - ruby-on-rails

I'm not sure what I'm missing here, can anyone point me in the right direction? I set up the initializers and the associations in the model but my JSON is still returning only the attributes inside Active record, including the created_at and updated_at attributes. What am I doing wrong?
ApplicationController.rb
class ApplicationController < ActionController::API
include CanCan::ControllerAdditions
include ActionController::Serialization
acts_as_token_authentication_handler_for User
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { render nothing: true, status: :forbidden }
end
end
end
CompanyController.rb
def show
#logo = Logo.find(params[:user_id])
render json: #logo
end
CompanyModel.rb
class Company < ActiveRecord::Base
belongs_to :user
has_one :logo, dependent: :destroy
has_many :cards, dependent: :destroy
end
LogoModel.rb
class Logo < ActiveRecord::Base
belongs_to :company
mount_uploader :image, ImageUploader
end
CompanySerializer.rb
module V1
class CompanySerializer < ActiveModel::Serializer
attributes :id, :user_id, :name
has_one :logo
end
end
LogoSerializer.rb
module V1
class LogoSerializer < ActiveModel::Serializer
attributes :id, :company_id, :image_url
end
end
EDIT: I should have posted the CompanyController Action, not the LogoController. I've added it above and added the LogoSerializer

Related

How to set up invoices with invoice_rows once an order is created?

I'm trying to figure out how to automatically set up an invoice with invoice_rows, once a reservation is saved.
Attempts
Before even including the order_rows, I tried generating an invoice for order:
I tried including #order.invoices.create(order_contact_id: #order.order_contact_id) after saving the order in create, but this resulted in an empty array:
Order.last.invoice => []
Afterwards I probably should iterate over all products belonging to a order and include them as invoice_rows in invoice. But not sure how.
Note
The actual structure is more complex and consequently I need all my tables.
Code
models
class Order < ApplicationRecord
has_many :invoices
has_many :order_products, dependent: :destroy
end
class OrderProduct < ApplicationRecord
belongs_to :product
belongs_to :order
accepts_nested_attributes_for :product
end
class Product < ApplicationRecord
has_many :orders, through: :order_products
has_many :product_prices, dependent: :destroy, inverse_of: :product
accepts_nested_attributes_for :product_prices, allow_destroy: true
end
class ProductPrice < ApplicationRecord
belongs_to :product, inverse_of: :product_prices
end
orders_controller
class OrdersController < ApplicationController
def create
#order = #shop.orders.new(order_params)
authorize #order
if #order.save
authorize #order
# #order.invoices.create(order_contact_id: #order.order_contact_id)
redirect_to new_second_part_shop_order_path(#shop, #order)
end
end
private
def order_params
params.require(:order).permit(:order_contact_id,
order_products_attributes: [:id, :product_id, :product_quantity, :_destroy,
products_attributes: [:id, :name, :description]])
end
end
As suggested in the comments, I found the error message by using #order.invoices.create!.
Afterwards I iterated over each product and created an invoice_row for the created invoice.
#invoice = #order.invoices.create!(order_contact_id: #order.order_contact_id)
#order.order_products.each do |o_product|
#invoice.invoice_rows.create!(
description: o_product.product.name,
total_price: #reservation.total_product_price(#reservation, o_product)
)
end

Loop through attributes in the custom validation method

I want to loop through attributes which are validated with custom validation method.
I have model Post which has_many :languages and a model Language which belongs_to :post. In the languages table I have columns - id, post_id, language.
Post model:
class Post < ApplicationRecord
has_many :languages
accepts_nested_attributes_for :languages, reject_if: :all_blank
validates_associated :languages
end
Language model:
class Language < ApplicationRecord
validate :unique_languages?
def unique_languages?
#LOOP ATTRIBUTES
end
end
IIn the Language model in the unique_languages? I want too loop through all language attributes of the post.
This is posts_controller with strong params and logic for creating a post:
class PostsController < ApplicationController
def new
#post = Post.new
#post.languages.build if #post.languages.empty?
end
def create
#post = Post.new(post_params)
#post.languages.build if #post.languages.empty?
if #post.save
redirect_to action: 'new'
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:title, languages_attributes: [:language])
end
end
## app/validators/nested_attributes_uniqueness_validator
class NestedAttributesUniquenessValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
raise ArgumentError if options[:parent].blank?
association = record.class.to_s.pluralize.underscore # :languages
items =
record # #language
.send(options[:parent]) # #language.post
.send(association) # #language.post.languages
.select(attribute) # #language.post.languages.select(:language)
unless items.distinct.size == items.size
record.errors.add attribute, :nested_attributes_uniqueness
# Don't forget to translate `errors.messages.nested_attributes_uniqueness`
end
end
end
## app/models/post.rb
class Post < ApplicationRecord
has_many :languages, inverse_of: :post # :inverse_of is important
accepts_nested_attributes_for :languages, reject_if: :all_blank
end
## app/models/language.rb
class Language < ApplicationRecord
belongs_to :post, inverse_of: :languages # :inverse_of is important
validates :language, nested_attributes_uniqueness: { parent: :post }
end
You can access a object attributes by:
class Language < ApplicationRecord
validate :unique_languages?
def unique_languages?
self.attributes.each do |attr, value|
#your code
end
end
end
your object.attributes return a hash where the keys are the attributes names, and the value is the value for that attribute. Don't know exactly what you're trying to do, but this may helps. Good luck!

Rails NoMethodError: undefined method `includes' for #<User:0x007f62dbbe62f8>

I'm using the active_model_serializers gem in my Rails 5 app. I created a few serializer files in /app/seralizers, user_serializer.rb, sector_serializer.rb, and slot_serializer.rb .
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :email, :phone, :admin, :auth_token, :organization_id
has_many :sectors
has_many :slots
has_many :elements
end
class SectorSerializer < ActiveModel::Serializer
attributes :id, :user_id, :sector_number, :title
belongs_to :user
has_many :slots
has_many :elements
end
class SlotSerializer < ActiveModel::Serializer
attributes :id, :user_id, :sector_id, :sector_number, :title, :slot_number
belongs_to :user
belongs_to :sector
has_many :elements
end
And in my controller code, I have:
class Api::V1::UsersController < API::V1::BaseController
respond_to :json
def sky
#user = User.find_by_id(params[:user_id]).includes(:sectors, :slots)
if #user
render json: #user
else
raise "Unable to get Sky"
end
end
end
My server is throwing an error at the line where I do the .includes and I can't figure out why.
Any help is appreciated. Thanks!
Change
#user = User.find_by_id(params[:user_id]).includes(:sectors, :slots)
to
#user = User.includes(:sectors, :slots).find_by_id(params[:user_id])
Point is you have to call includes on a class (that inherits from ActiveRecord::Base/ApplicationRecord), not a single user object.

Rails fires N+1 queries for polymorphic associations

I am using Rails 5.0.1, and am really confused about the following problem. I have few models with polymorphic associations.
class Container < ApplicationRecord
has_many :steps, as: 'parent', dependent: :destroy
end
class Step < ApplicationRecord
belongs_to :parent, polymorphic: true
belongs_to :implementation, polymorphic: true
end
class FirstStep < ApplicationRecord
has_one :step, as: 'implementation'
has_many :params, dependent: :destroy
end
class SecondStep < ApplicationRecord
has_one :step, as: 'implementation'
has_many :headers, dependent: :destroy
end
class Param < ApplicationRecord
belongs_to :first_step
end
class Header < ApplicationRecord
belongs_to :second_step
end
A step associates to an implementation (FirstStep, SecondStep). In addition to it, a container can also be a step's implementation. I'm using Active Model Serializers to serialize the model info to JSON. Following is the related code to serializers.
class StepSerializer < ActiveModel::Serializer
attributes :id, :implementation_type, :implementation_id, :active, :position
belongs_to :implementation
end
class FirstStepSerializer < ActiveModel::Serializer
attributes :name, :params_attributes
def params_attributes
object.params.map { |p| ParamSerializer.new(p).attributes }
end
end
class SecondStepSerializer < ActiveModel::Serializer
attributes :id, :title, :headers_attributes
def headers_attributes
object.headers.map { |p| HeaderSerializer.new(p).attributes }
end
end
class ParamSerializer < ActiveModel::Serializer
attributes :id
end
class HeaderSerializer < ActiveModel::Serializer
attributes :id
end
The implementations of step model can have different attributes, as specified in the model. The problem is, when I write
render json: container.steps
it fires N+1 queries to get the results. How do I optimize it?
Edit 1
Inspired by this answer, I tried to separate objects by their implementation_type, and it worked. What I did was:
# my controller action
def index
steps = []
steps += container.steps.where(implementation_type: 'FirstStep').includes(implementation: [:params])
steps += container.steps.where(implementation_type: 'SecondStep').includes(implementation: [:headers])
render json: steps
end
This prevented the N+1 queries for fetching params and headers, but it doesn't work if a step is a container.
Change your FirstStepSerializer and SecondStepSerializer serializer like following
class FirstStepSerializer < ActiveModel::Serializer
attributes :name
has_many :params, :serializer => ParamSerializer
end
class SecondStepSerializer < ActiveModel::Serializer
attributes :id, :title
has_many :headers, :serializer => HeaderSerializer
end
This might help

STI & Polymorphic association & user_id

So I have an assets model which is inherited by images and files as STI. Products has many images as assetable. This polymorphic association is working fine but I can't figure out how to add the user_id to every asset. A asset always belongs to a user no matter if its an image or file. Please look at my code below. I'm pretty sure there's something I need to do within the controllers create method but I'm not sure what? I've tried merging :user_id but this just returns nil when looking at the console log. Ideally what I'd also like to do is #user.images which will display all the images that the user has uploaded or even #user.files
class User < ActiveRecord::Base
has_many :products
has_many :assets
end
class Asset < ActiveRecord::Base
belongs_to :assetable, :polymorphic => true
belongs_to :user
end
class Image < Asset
mount_uploader :filename, ImageUploader
attr_accessible :filename, :assets
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :images, as: :assetable
accepts_nested_attributes_for :images
attr_accessible :name, :price, :images_attributes
validates_presence_of :name
end
class ProductsController < ApplicationController
def create
#product = Product.new(params[:product])
#product.user_id = current_user.id
params[:product][:images_attributes].each do |key, image|
# image.user_id = current_user.id
image.merge(:user_id => 1)
end
#product.save
end
end
def create
params[:product][:images_attributes].each do |key, image|
image.merge!(:user_id => current_user.id)
end
#product = Product.new(params[:product])
#product.user_id = current_user.id
#product.save
end
By the way, you'd better to replace inheritance logic into the model:
class Image < Asset
mount_uploader :filename, ImageUploader
attr_accessible :filename, :assets
before_save do
self.user = assetable.try(:user)
end
end
# controller
def create
#product = Product.new(params[:product])
#product.user = current_user
#product.save
end

Resources