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:

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?
  • How do I write the migration(s) to bring the db in sync with the
    revised models?
  • Should I implement this as three separate migrations?

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.

(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?)

TIA.

  • ff

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
http://www.marnen.org
[email protected]

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

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 Laibow-Koser
http://www.marnen.org
[email protected]

@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

@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

mysql>describe line_items;
describe line_items;
±------------±-------------±-----±----±--------±---------------+
| Field | Type | Null | Key | Default | Extra |
±------------±-------------±-----±----±--------±---------------+
| id | int(11) | NO | PRI | NULL | auto_increment
|
| product_id | int(11) | NO | MUL | NULL |
|
| order_id | int(11) | NO | MUL | NULL |
|
| quantity | int(11) | NO | | NULL |
|
| total_price | decimal(8,2) | NO | | NULL |
|
| created_at | datetime | YES | | NULL |
|
| updated_at | datetime | YES | | NULL |
|
±------------±-------------±-----±----±--------±---------------+
7 rows in set (0.00 sec)

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]