noobish: create migration to add foreign keys with :through attrs?

I'm dipping my toe into the world of data warehousing and dimensional databases. In:    http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html I was excited to find the following under the "many to many" section -- it's the pattern I'm looking for:

fearless_fool wrote:

I'm dipping my toe into the world of data warehousing and dimensional databases. In:    http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html I was excited to find the following under the "many to many" section -- it's the pattern I'm looking for: --------   class Assignment < ActiveRecord::Base     belongs_to :programmer # foreign key - programmer_id     belongs_to :project # foreign key - project_id   end   class Programmer < ActiveRecord::Base     has_many :assignments     has_many :projects, :through => :assignments   end   class Project < ActiveRecord::Base     has_many :assignments     has_many :programmers, :through => :assignments   end -------- So far so good -- I created empty models for these via     foreach m (Assignment Programmer Project)     script/generate model $m     end and then went back and hand-annotated the model files as shown above.

My questions:

* Could ActiveRecord handle more of the model creation in this case? If so, how?

ActiveRecord handles none of the model creation, as far as I can tell. If you're asking about whether you could have script/generate model do more, the answer is no, and you probably wouldn't want to in any case. Excessive generated code is generally a maintenance problem.

* How do I write the migration(s) to bring the db in sync with the revised models?

script/generate model already does that for you. Look in db/migrate.

* Should I implement this as three separate migrations?

If it's one atomic change, then put it in one migration. One migration file should contain everything that is necessary to bring the database from one consistent state to the next consistent state.

The guide in Active Record Migrations — Ruby on Rails Guides shows an example (grep ExampleMigration), but I'm not feeling sure footed enough to try it unchaperoned.

Live up to the fearless part of your name and try it! If you have problems, please ask, but give it a try first.

(As an aside, is there some nifty reference that shows the mapping between ActiveRecord descriptors and the equivalent SQL? Or should I dig through the sources to figure that out?)

There's nothing conventionally called an "ActiveRecord descriptor". What do you mean here? And have you read the rdoc and guide on ActiveRecord associations?

TIA.

- ff

Best,

Marnen Laibow-Koser wrote: [...]

* Could ActiveRecord handle more of the model creation in this case? If so, how?

ActiveRecord handles none of the model creation, as far as I can tell. If you're asking about whether you could have script/generate model do more, the answer is no, and you probably wouldn't want to in any case. Excessive generated code is generally a maintenance problem.

* How do I write the migration(s) to bring the db in sync with the revised models?

script/generate model already does that for you. Look in db/migrate.

A bit more about these two points. If you type "script/generate model" at the command line, you will see help text talking about options. You *can* specify field names and types which will be written into the generated migration file. If you don't do this, then the migration will still be generated, but it will not contain any field names.

Best,

fearless_fool wrote:

I'm dipping my toe into the world of data warehousing and dimensional databases. In:    http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html I was excited to find the following under the "many to many" section -- it's the pattern I'm looking for: --------   class Assignment < ActiveRecord::Base     belongs_to :programmer # foreign key - programmer_id     belongs_to :project # foreign key - project_id   end   class Programmer < ActiveRecord::Base     has_many :assignments     has_many :projects, :through => :assignments   end   class Project < ActiveRecord::Base     has_many :assignments     has_many :programmers, :through => :assignments   end

[...]

One more thing! It occurs to me that you should know that despite what the Rails Guides say, you don't have to write raw SQL to get foreign key constraints in Rails. Use

or one of the forks of foreign_key_migrations (which is perhaps better designed than Foreigner, but seems to be sort of abandoned).

Best,

@Marmen:

Thanks much for the pointers and guidance. (In retrospect, I really could have asked "when is it appropriate to hand-edit a subclass of ActiveRecord?", and the answer, I think is "almost never, for simple cases".) With this new found understanding, Matthew Higgins's Foreigner package makes sense.

- ff

fearless_fool wrote:

@Marmen:

(Please note the correct spelling of my name. :slight_smile: )

Thanks much for the pointers and guidance.

You're welcome!

(In retrospect, I really could have asked "when is it appropriate to hand-edit a subclass of ActiveRecord?", and the answer, I think is "almost never, for simple cases".)

Where the heck did you get that idea? A large part of Rails development is hand-editing your ActiveRecord model classes.

Or were you talking about using raw SQL in a migration?

In general, anything in Rails can be hand-edited (except db/schema.rb). Rails' generators don't produce generated code in the sense that Java frameworks tend to -- they just make boilerplate skeletons to save you a little typing.

  With this new found understanding, Matthew Higgins's Foreigner package makes sense.

- ff

Best,

@Marnen: first, apologies on the misspelling -- no slight intended.

After thrashing around on this forum and working through AWDwR, I can pose my question more succinctly. But bear with me if I still get it wrong...

* You use migrations to define / extend / modify the tables in your db.

* ActiveRecord dynamically analyzes the tables in your db and from that, builds glue routines between Ruby and your db.

If that's the case, why does one need to include belongs_to, has_many, has_one (etc) in subclasses of ActiveRecord? Since the relationships between tables are described by the tables themselves, it seems blatantly un-DRY to specify such relationships in two places. (My belief that all Rails code was DRY may have been my source of confusion in previous posts...)

Case in point: the line_item table in the AWDwR depot example appears as follows. Can't Rails deduce the LineItem#belongs_to, the Product#has_many and the Order#has_many relationships from this?

- ff

describe line_items;

describe line_items;

fearless_fool wrote:

@Marnen: first, apologies on the misspelling -- no slight intended.

None assumed.

After thrashing around on this forum and working through AWDwR, I can pose my question more succinctly. But bear with me if I still get it wrong...

* You use migrations to define / extend / modify the tables in your db.

Right.

* ActiveRecord dynamically analyzes the tables in your db and from that, builds glue routines between Ruby and your db.

Not really. ActiveRecord looks at the fields in each table and uses that information to build SQL queries and return the results as Ruby objects. That's basically it.

If that's the case, why does one need to include belongs_to, has_many, has_one (etc) in subclasses of ActiveRecord?

Because ActiveRecord doesn't automatically detect table relationships. Indeed, without foreign key constraints (which not all databases support), there is *no way* that it could conceivably do so reliably.

Since the relationships between tables are described by the tables themselves, it seems blatantly un-DRY to specify such relationships in two places.

The relationships are actually not described by the tables in all cases, at least not in a way that is susceptible to deduction.

(My belief that all Rails code was DRY may have been my source of confusion in previous posts...)

That belief is not 100% correct. :slight_smile:

Case in point: the line_item table in the AWDwR depot example appears as follows. Can't Rails deduce the LineItem#belongs_to, the Product#has_many and the Order#has_many relationships from this?

No.

- ff

Best,