How to access associated model through another mod

2020-07-24 05:34发布

问题:

UPDATE (specific and more detailed previous version is below):

I'm developing a TV station web site. Here are requirements for my Program section:

  1. Each Program has ONE Category.
  2. Each Program has ONE Subcategory.
  3. Each Category has MANY Subcategories
  4. Each Category has MANY Programs.
  5. Each Subcategory has ONE Category
  6. Each Subcategory has MANY Programs.

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

回答1:

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

  1. Each Program has ONE Category.
  2. Each Program has ONE Subcategory.
  3. Each Category has MANY Subcategories
  4. Each Category has MANY Programs.
  5. Each Subcategory has ONE Category
  6. 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.