Reputation: 2900
I've got below model structure where I stored Books and SellingInfo (which represents presale and after sale costs):
class Books < ApplicationRecord
STATUSES = %w[planned purchased].freeze
has_many :selling_infos, dependent: :destroy
end
class SellingInfo < ApplicationRecord
belongs_to :book
end
Because of ActiveAdmin requirements I want to have new relation to a model which will be the same as selling_infos
table (the same columns, types etc.) but I'll use it only when Book.status == 'purchased'
. In order not to overload the database I don't want to create new model, let's say purchased_selling_info
, with the same data structure but create some kind of 'fake model' and use it as purchased_selling_info
.
I was trying to add something like below to Book model:
# models/book.rb
has_one :purchased_selling_info,
-> { where(status: 'purchased') },
class_name: 'SellingInfo',
dependent: :destroy
But when I'm trying check if it works in rails console I'm getting an error:
2.7.2 :009 > Book.purchased_selling_info.size
NoMethodError (undefined method `purchased_selling_info' for #<Class:0x00007f9916f33200>)
Upvotes: 1
Views: 305
Reputation: 102240
The solution to storing records with different behaviors in one table is Single Table Inheritance. By adding a type
column you can have different assocations:
class Book < ApplicationRecord
has_many :selling_infos, dependent: :destroy
end
class PurchasedBook < Book
has_one :purchased_selling_info,
class_name: 'SellingInfo',
dependent: :destroy
end
When you load the records from the DB ActiveRecord reads the inheritance column (which is type
by default) and will initialize that class.
If you don't want to use STI you can restrict the creation of purchased_selling_info
either through custom validations or association callbacks.
class SellingInfo
validates :validates_is_purchased, if: ->{ |si| si.purchased? }
def validates_is_purchased
errors.add(:book, 'wrong book type')
end
end
What you're currently doing wont work since:
Book.purchased_selling_info.size
gives a NoMethod error since you're calling an instance method on the class.In order not to overload the database I don't want to create new model, let's say purchased_selling_info, with the same data structure but create some kind of 'fake model' and use it as purchased_selling_info.
You should carefully weigh "overloading the database" against the added complexity and mainence cost of a hacky solution. Sounds like a case of premature optimization to me.
Upvotes: 2