Reputation: 7960
UPDATE (specific and more detailed previous version is below):
I'm developing a TV station web site. Here are requirements for my Program section:
Program
has ONE Category
.Program
has ONE Subcategory
.Category
has MANY Subcategories
Category
has MANY Programs
.Subcategory
has ONE Category
Subcategory
has MANY Program
s.I want to retrieve all these three models to be associated. For example, I should be able to retrieve below data from my views:
While:
p = Program.find(1)
p_cat = ProgramCategory.find(1)
p_subcat = ProgramSubcategory.find(1)
I should be able to retrieve and also EDIT these:
p.program_category
p.program_subcategory
or
program_category.programs
program_subcategory.programs
You can see what I tried below to achieve these requirements. You may recommend me a totally different way or fix my mistakes.
Thank you
============================================================
I have 3 models. They are supposed to be nested in eachother.
ProgramCategory > ProgramSubcategory > Program
Here are my codes:
ProgramCategory model:
has_many :programs
has_many :program_subcategories
ProgramSubcategory model:
belongs_to :program_category
has_many :programs
Program Model:
belongs_to :program_category
belongs_to :program_subcategory
As I create a new Program, I can set its Category and everything is fine. I can access them from both sides. For example,
program.program_category
gives me what I expected. and also
program_category.programs
gives me what I want to have, too.
BUT, -here comes the question-
When I try to access program.program_subcategory
, I receive just a nil.
Eventhough my Subcategory's Category is set and my Program's Category is set too, why I can't access program.program_subcategory
directly?
When I type program_category.program_subcategories
, I receive all Subcategories owned by that Category. But I CAN NOT get Subcategories from directly a Program
object.
My scheme is below. Any help is appriciated.
ActiveRecord::Schema.define(:version => 20120926181819) do
create_table "program_categories", :force => true do |t|
t.string "title"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "program_subcategories", :force => true do |t|
t.integer "program_category_id"
t.string "title"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "program_subcategories", ["program_category_id"], :name => "index_program_subcategories_on_program_category_id"
create_table "programs", :force => true do |t|
t.integer "program_category_id"
t.integer "program_subcategory_id"
t.string "title"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "programs", ["program_category_id", "program_subcategory_id"], :name => "my_join1", :unique => true
end
Upvotes: 2
Views: 3341
Reputation: 2386
The design is strange a bit. If you need nesting like
ProgramCategory > ProgramSubcategory > Program
then you need
class Program < ActiveRecord::Base
belongs_to :program_subcategory
end
class ProgramSubcategory < ActiveRecord::Base
belongs_to :program_category
has_many :programs
end
class ProgramCategory < ActiveRecord::Base
has_many :programs, :through => :program_subcategories
has_many :program_subcategories
end
This way when you create a program you can assign a subcategory to it. And this subcategory is already assigned to category, so you can access it via program.program_subcategory.program_category
And you do not need program_category_id
foreign key in programs
because program is not connected to category directly, but via subcategory.
UPDATE
- Each Program has ONE Category.
- Each Program has ONE Subcategory.
- Each Category has MANY Subcategories
- Each Category has MANY Programs.
- Each Subcategory has ONE Category
- Each Subcategory has MANY Programs.
Then I believe that my answer is still valid. You see, my structure is the same as your description except Each Program has ONE Category
(because rails has no belongs_to through
). you has one
is actually belongs_to
(because it can belong to only one).
But as soon as Each Program has ONE Subcategory
and Each Subcategory has ONE Category
program's subcategory's category will be the ONLY program's category. You can have p.program_category
by defining a method on a Program class:
def program_category
program_subcategory.program_category
end
Now for the part of
I should be able to retrieve and also EDIT these:
p.program_category
Imagine you have a Program in subcategory Comedy from category Movies.
You say you want to be able to EDIT programs category directly (if I understood correctly), like this:
p.program_category = ProgramCategory.find_by_name("Sports")
But what you expect to be program's subcategory then? As soon as Sports have many subcategories? Do you expect it to be blank?
So in this design the only way to change program's category is to change program's subcategory:
p.program_subcategory = ProgramSubcategory.find_by_name("Tennis")
And this will manke program's category == Sports, because Tennis belongs to Sports.
Note: If you really want sometimes to change program's category directly, leaving its subcategory blank it requires another design of course. I do not think it is very difficult but it requires more work with less help from Rails AR Associations magic.
Upvotes: 2