Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify unique attributes for child models in rails using STI

I plan to use STI in Rails with the following models:

class Promo < ActiveRecord::Base
end

class Event < Promo
end

class Discount < Promo
end

There are just a couple of differences in terms of attributes between Event and Discount so I figure STI is a good way to go.

I'm not sure how to ensure that, for example, only Event has an additional image_filename attribute. I understand that it will be in the promos table, and that it needs to be NULL-able in case I insert a Discount row. How do I ensure that a Discount object knows nothing about the image_filename attribute (i.e. is not listed in Discount.column_names and/or can't set it) and that Event knows about it?

like image 629
kubasub Avatar asked Dec 11 '14 15:12

kubasub


2 Answers

I think that the concept is different, while your Promo class inherit from ActiveRecord::Base then all child classes inherit its attributes, so, the column names are shared in all the STI. but you still can set the column image_filename as null in the DB and validate it in your models, so you will be able to store and update the record based in your case with something like this:

class Promo < ActiveRecord::Base
  #Common validations
  validates :name, presence: true
  validates :user, presence: true
end

class Event < Promo
  #event related validations
  validates image_filename, presence: true
end

class Discount < Promo
  #discount related validations
  validates percentage, presence: true
end

Here is a great tutorial http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-1/

like image 155
rderoldan1 Avatar answered Nov 07 '22 14:11

rderoldan1


Having STI setup means you'll have 2 different forms for each child Class. That means you'll have fields available in the form accordingly for Event and for Discount, so when you'll save a Discount record the second image_file field will have a nil value by default (if you didn't changed it to something else).

Now, all attributes of your Promo class are actually methods that you can override, so if in Discount you'll do:

def additional_image_filename #name of the attribute
  nil
end

it will always return nil as result no matter what it was saved there. An example in console based on my app where I override the name attribute (not that I need, just showing):

2.1.5 :028 > Recipe.last
  Recipe Load (0.2ms)  SELECT  "recipes".* FROM "recipes"   ORDER BY "recipes"."id" DESC LIMIT 1
 => #<Recipe id: 4, name: "Potatoes Au Gratin", instructions: nil, created_at: "2014-12-04 12:54:26", updated_at: "2014-12-04 12:54:26"> 
2.1.5 :029 > Recipe.last.name
  Recipe Load (0.4ms)  SELECT  "recipes".* FROM "recipes"   ORDER BY "recipes"."id" DESC LIMIT 1
 => nil 

as you can see there is a name in database and it has a value, but it returns nil cause it was overridden.

Also you could add validations for separate Classes or callbacks that will make sure to clean up the attributes you don't need like the so called additional_image_filename, but I don't see why you'd need this.. cause for each form you'll have separate controllers and actions I guess and therefore there will be different permitted_params that will let you choose what fields should allowed to be saved only.

In short:

  1. Don't include additional_image_filename in form for Discount
  2. Don't include additional_image_filename in peritted_params in controller action for Discount
  3. Set the value as nil in db or override the method in Discount class as described above.
like image 35
rmagnum2002 Avatar answered Nov 07 '22 12:11

rmagnum2002