Hi Guys,
I’ve been using Rails for the first time and have encountered some
issues that I’m not sure are a result of my inexperience with Rails or a
design oversight.
In my domain model, a School has an Address. In SQL, I’ve defined a
“schools” table and an “addresses” table, and the “schools” table
defines a column called “address_id” that refers to a row in the
“addresses” table.
When I model this using ActiveRecord, it seems that I’m meant to put a
line into the School model that looks like this:
class School < ActiveRecord::Base
belongs_to :address
end
and a line into the Address model that looks like this:
class Address < ActiveRecord::Base
has_many :schools
end
First of all, this seems unintuitive, since you don’t naturally think of
a School “belonging” to an Address, but the user documentation says that
the “belongs_to” clause should reside in the class whose table contains
the foreign key (in this case, “address_id”).
Second, when a School is destroyed I want its corresponding Address to
be automatically destroyed but I can’t do this using the above
declaration because the :dependent mechanism attaches to the :has_many
clause. In other words, I can arrange for a School to be destroyed when
its Address is destroyed, but not the other way around!
Am I missing something here, or is this a Rails design problem? If it’s
the latter, I highly recommend that it’s fixed before Rails goes to
version 1.0.
Regards,
Graham
If more than one school can belong to an address then do you really want
to
delete the address when you delete a school? Perhaps you really want
“has
one :school” in your school model. If so…
I haven’t tried this code and I’m no expert…
class School < ActiveRecord::Base
belongs_to :address
use a callback to delete the address after the school has been
successfully deleted.
def after_destroy
Address.delete(address.id)
end
end
Rails book pages 224 & 275
Hi Peter,
I think you’re right that the School can use “has_one” to ensure a 1-1
mapping between School and Address, but the School still has to be the
one with the “belongs_to” statement since it has the foreign key to the
address. This means that the “ownership” is the wrong way around, and I
still can’t arrange for the Address to be destroyed when the School is
destroyed.
The example in the Ruby on Rails book by Dave T. uses an example
where an “Invoice” belongs to an “Order”. This is appropriate use of the
word “belongs_to”, but it seems that maybe the ActiveRecord design was
built around examples like that one and does not take into account other
(quite common) situations like my School example where the “ownership”
is in fact in the opposite direction.
Also, you’re right, I can work around this issue by using callbacks, but
the whole point of frameworks like Rails is to codify common design
patterns so you don’t have to do this kind of work manually.
On 11/26/05, Sanford SV [email protected] wrote:
Try using ‘has_one :address’ in the School model and ‘belongs_to:’ in
the Address.
In the School table you can leave out the address_id, just add a
‘school_id’ in the Address record.
‘:dependent’ should work fine then.
I’d agree with Sanford, School :has_one Address makes more sense, but
if it’s a one-to-one relationship why couldn’t you just have the
school’s address in the schools table?
I’d be quite interested in what project you’re working on actually.
I’m currently rebuilding a PHP based school management system in Rails
and could offer some insights into the DB design. Rails is a dream
until you run into 3-way bridge tables for example.
Mark B.
Easy Schedule Management
http://easy-online-schedule.com
Your approach would work in this particular case, but it’s very likely
that other entities will also have addresses. Rather than hardwiring a
back reference from the Address to a School, it seems like better
database design practice for higher-level tables to refer to lower-level
tables rather than the other way around.
In other words, I don’t want to warp my database structure to fit the
ActiveRecord framework, I want ActiveRecord to support good relational
database design.
Regards,
Graham
If ‘address’ is just an attribute of ‘School’ there’s no need to store
an explicit pointer to the address record in the ‘School’ record.
Typically you’d store the school_id in the Address record and do the
lookup from that direction (leave the back-association in the dependent
info, not the primary info).
Try using ‘has_one :address’ in the School model and ‘belongs_to:’ in
the Address.
In the School table you can leave out the address_id, just add a
‘school_id’ in the Address record.
‘:dependent’ should work fine then.
beattie.mark wrote:
On 11/26/05, Sanford SV [email protected] wrote:
Try using ‘has_one :address’ in the School model and ‘belongs_to:’ in
the Address.
In the School table you can leave out the address_id, just add a
‘school_id’ in the Address record.
‘:dependent’ should work fine then.
I’d agree with Sanford, School :has_one Address makes more sense, but
if it’s a one-to-one relationship why couldn’t you just have the
school’s address in the schools table?
I’d be quite interested in what project you’re working on actually.
I’m currently rebuilding a PHP based school management system in Rails
and could offer some insights into the DB design. Rails is a dream
until you run into 3-way bridge tables for example.
Mark B.
Easy Schedule Management
http://easy-online-schedule.com
Hi Mark,
You could embed the address into the school table, but since addresses
are commonly occuring entities (students can have an address as well for
example), it makes more sense to have a separate table for addresses.
I’m working on an online education system that was prototyped in PHP and
is now being ported to Rails. It focuses on the teaching/learning part
of the equation rather than the school management stuff.
Regards,
Graham
The example in the Ruby on Rails book by Dave T. uses an example
where an “Invoice” belongs to an “Order”. This is appropriate use of the
word “belongs_to”, but it seems that maybe the ActiveRecord design was
built around examples like that one and does not take into account other
(quite common) situations like my School example where the “ownership”
is in fact in the opposite direction.
Absolutely. So your only problem is the choice of the word “belongs
to” to describe the functionality, not the functionality itself?
Perhaps you could just ignore this very small problem and it will go
away?
Since people seem to run into this “problem” all the time, maybe the
belongs_to method should be aliased to some other method with a name
that infers the opposite ownership.
Regards,
Tomas J.
Would it be reasonable to change which table has the foreign key in
it? Would that make the belongs_to more intuitive?
Could you reasonably combine school and address into one table
(assuming a one to one link between the two)? Is a 1:1 link not
generally a sign that, perhaps the two tables should really be one?
I know there are reasons to split a table into two 1:1 tables - just
a suggestion.
bruce
Do it the other way
Addresses belongs_to :school and have a school_id
Schools then has_one :address
if you don’t need to normalize, don’t. there’s such a thing as over
normalizing.
there’s nothing wrong with putting the address in the schools table.
now it
may make sense to normalize a students table, where it’s quite possible
that
a student could have more than one address (say mom/dad live at separate
addresses, etc). in this case, yes, definitely normalize the address
out to
a separate table. but for the case of a school which has one and only
one
address, it’s perfectly fine to leave the address in the school table.
that
would not be a case of bad design.
With the “belongs_to” and “has_many” relationship the way you have it
allows
for several schools to have one address. If you reverse it then a school
could have multiple addresses. you could use has and belongs to many for
both ways. This is a design choice.
Hi Peter,
I think you’re right that the School can use “has_one” to ensure a 1-1
mapping between School and Address, but the School still has to be the
one with the “belongs_to” statement since it has the foreign key to the
address. This means that the “ownership” is the wrong way around, and I
still can’t arrange for the Address to be destroyed when the School is
destroyed.
I don’t find the belongs_to statement so offensive but if it is
one-to-one
then the foreign key can be moved to the addresses table.
The example in the Ruby on Rails book by Dave T. uses an example
where an “Invoice” belongs to an “Order”. This is appropriate use of the
word “belongs_to”, but it seems that maybe the ActiveRecord design was
built around examples like that one and does not take into account other
(quite common) situations like my School example where the “ownership”
is in fact in the opposite direction.
This is a guess but I think the :dependent => true mirrors the SQL
“cascade
on delete”. Since there isn’t something in SQL that can go in the other
table to reverse the “cascade” (like you want) then there isn’t
something in
active record that you want. I think it is because of the multiple
schools/one address type of issue. So you use the magic of callbacks…
Also, you’re right, I can work around this issue by using callbacks, but
the whole point of frameworks like Rails is to codify common design
patterns so you don’t have to do this kind of work manually.
I don’t think a callback is a work around. It is just the way it is
done.
Callbacks offer more flexibility. If you keep your current has_many
arrangement then the after_destroy call back could delete the address if
the
school being deleted was the last one to have that address.
Hi Guys,
I really appreciate all the comments here! Although there are several
work-arounds for my specific case suggested, it still seems that
ActiveRecord in its current form does not support a very basic design
pattern that would commonly occur in good data database design.
In my case, the suggestions were:
- embed the address information into the school table,
thereby avoiding the issue entirely
- embed a school_id into the address table
Both would work in my specific application as it stands right now, but
that is just skirting the issue rather than recognizing the underlying
problem and dealing with it.
For example, if I want to keep a separate addresses table so that, say,
both schools and students could maintain addresses using the same
address schema, then neither of these suggestions works. I guess I could
keep an untyped “owner_id” in the address that refers back to an untyped
foreign key of whoever owns the address, but that seems gross. It seems
more natural for the owner of the address to refer to the address. So
I’d have an “address_id” in the “schools” table and an “address_id” in
the students table.
When I delete a school, I expect its address to be deleted if I indicate
a dependency. That’s simple for a framework to do; just use the foreign
key to delete the child.
I like Rails a lot, and one of its strengths is its ability to support
applications naturally and powerfully. Not being able to support the
straightforward table design I outlined above seems to be a weakness in
ActiveRecord that could probably be fixed pretty easily.
Regards,
Graham
On Sat, 2005-11-26 at 08:17 +0100, Graham G. wrote:
has_many :schools
clause. In other words, I can arrange for a School to be destroyed when
its Address is destroyed, but not the other way around!
Am I missing something here, or is this a Rails design problem? If it’s
the latter, I highly recommend that it’s fixed before Rails goes to
version 1.0.
Try this:
http://www.robbyonrails.com/articles/2005/10/19/new-active-record-options-for-associations
RubyURL: http://rubyurl.com/bE4
-Robby
–
/******************************************************
On Saturday 26 November 2005 21:06, Peter M. wrote:
end
if someone knows how to do this please post it.
end
Why not use inheritance to resolve this? Assuming that I understand
the problem correctly. Have a StudentAddress and SchoolAddress which
inherit the Address but are linked to the appropriate tables, or am
I missing something?
Cheers,
I think active record can do what you want. Untested code but I’ve done
something just like this attaching images to multiple tables.
TABLES
schools
id
students
id
addresses
owner_class varchar
owner_id
MODELS
class Student < ActiveRecord::Base
has_one :address, :foreign_key => ‘owner_id’, :conditions =>
“owner_class
= #{self.to_s}”, :dependent => true
end
class Student < ActiveRecord::Base
has_one :address, :foreign_key => ‘owner_id’, :conditions =>
“owner_class
= #{self.to_s}”, :dependent => true
end
class address < ActiveRecord::Base
as far as I know address cannot belong_to anything because the
referenced table is variable.
if someone knows how to do this please post it.
end
petermichaux wrote:
I think active record can do what you want. Untested code but I’ve done
something just like this attaching images to multiple tables.
TABLES
schools
id
students
id
addresses
owner_class varchar
owner_id
This would probably work better as a join table
but those are mainly useful when an entity may
have multiple addresses (which, I suppose, is
possible)
E
Graham G. [email protected] <graham.glas wrote:
Hi Guys,
In my case, the suggestions were:
- embed the address information into the school table,
thereby avoiding the issue entirely
- embed a school_id into the address table
Both would work in my specific application as it stands right now, but
that is just skirting the issue rather than recognizing the underlying
problem and dealing with it.
I have to say that I agree very much with this. What about something
that would have a language attribute, so that you could choose English,
Spanish, etc
table thingy
id, name, language_id
table language
id, lang
Now, that seems to me to be the natural way to accomplish this. It
lends itself well to creating a pulldown of Language.find(:all).map {|l|
[l.name, l.id]}
It seems counter-intuitive to reverse this logic and attach the thingy
to the language table by creating a thingy_id…
An alternative would be to use a join table though
I do not feel this is correct, either, unless you
allow entities to have multiple addresses which is
certainly feasible.
The join table probly reflects the complexity of real life better as
students will have many adresses and one address may have many students.
Even a school could have several addresses. Eg a school could be moving
bewen two sites and have boath addreses valid.
Many (moast?)things tern out to be many to many relationaips in the
moast general case. If you expand all realtionships as many to many
then thigs get way to complicated and over general. The trick is to
know which ones to represient in this way when…
Graham G. [email protected] <graham.glas wrote:
Hi Guys,
I really appreciate all the comments here! Although there are several
work-arounds for my specific case suggested, it still seems that
ActiveRecord in its current form does not support a very basic design
pattern that would commonly occur in good data database design.
Well, it is not so much that it is not supported
than that the naming convention is somewhat awkward
–which I certainly understand: belongs_to and has_one
are precisely inverted in my opinion as well
In my case, the suggestions were:
- embed the address information into the school table,
thereby avoiding the issue entirely
- embed a school_id into the address table
Both would work in my specific application as it stands right now, but
that is just skirting the issue rather than recognizing the underlying
problem and dealing with it.
For example, if I want to keep a separate addresses table so that, say,
both schools and students could maintain addresses using the same
address schema, then neither of these suggestions works. I guess I could
keep an untyped “owner_id” …
An alternative would be to use a join table though
I do not feel this is correct, either, unless you
allow entities to have multiple addresses which is
certainly feasible.
When I delete a school, I expect its address to be deleted if I indicate
a dependency. That’s simple for a framework to do; just use the foreign
key to delete the child.
You can either have this be the responsibility of
the database or program logic (which I favour).
Choosing the latter, you would simply hook into
the event of a School being destroyed and issue
a destroy on the address as well. The former you
can handle at the DB level.
Regards,
Graham
E