:dependent => :destroy with nonstandard :foreign_key?

Assume the following contrived (but plausible) geometric model in which every Edge has two Vertices, every Vertex has many edges.

So I know it's bad form to answer your own question, but I realized this must be a job for callbacks, so I RTFM'd. My new definition for Vertex is:

class Vertex < ActiveRecord::Base   before_destroy do |record|     Edge.destroy_all "vertex_a_id = #{record.id}"     Edge.destroy_all "vertex_b_id = #{record.id}"   end end

It appears to do exactly what you'd expect: deleting a Vertex deletes all connected Edges.

Note that I also deleted the ":has_many edges" association for the same reason: Edge lacks the vertex_id field that AR depends upon to make the association, so you can't ask for "va.edges()". But it's simple enough to mimic its functionality, as in:

class Vertex   def edges     Edge.find_all_by_vertex_a_id(self.id) + Edge.find_all_by_vertex_b_id(self.id)   end end

Thanks for listening... - ff

Fearless Fool wrote:

So I know it's bad form to answer your own question, but I realized this must be a job for callbacks, so I RTFM'd. My new definition for Vertex is:

class Vertex < ActiveRecord::Base   before_destroy do |record|     Edge.destroy_all "vertex_a_id = #{record.id}"     Edge.destroy_all "vertex_b_id = #{record.id}"   end end

It appears to do exactly what you'd expect: deleting a Vertex deletes all connected Edges.

Great! You can probably unify those into one query and get better performance at the expense of a bit of Rails magic.

Note that I also deleted the ":has_many edges" association for the same reason: Edge lacks the vertex_id field that AR depends upon to make the association, so you can't ask for "va.edges()". But it's simple enough to mimic its functionality, as in:

class Vertex   def edges     Edge.find_all_by_vertex_a_id(self.id) + Edge.find_all_by_vertex_b_id(self.id)   end end

...which you can now probably use a variant of in :finder_sql to restore your has_many :edges. Again, you may be able to refactor it into one query.

Thanks for listening... - ff

Best,

Resurrecting an old thread because it has a useful lesson:

Marnen Laibow-Koser wrote in post #927576:

Fearless Fool wrote:

[stuff about a before_destroy method using two destroy_all calls]

Great! You can probably unify those into one query and get better performance at the expense of a bit of Rails magic.

[stuff about an edges method that concatenates two arrays]

...which you can now probably use a variant of in :finder_sql to restore your has_many :edges. Again, you may be able to refactor it into one query.

So as usual, Marnen is right (if not ahead of his time). Using the scoping constructs in Rails 3.0, this all becomes elegant and simple:

  class Edge << ActiveRecord::Base     belongs_to :vertex_a, :class_name => 'Vertex', :foreign_key => 'vertex_a_id'     belongs_to :vertex_b, :class_name => 'Vertex', :foreign_key => 'vertex_b_id'     scope :connected_to, lambda (vertex) {       where("edges.vertex_a_id = ? OR edges.vertex_b_id = ?", vertex.id, vertex.id     }   end

  class Vertex < ActiveRecord::Base     # caution: we are using delete_all for efficiency     before_destroy { |vertex| Edge.connected_to(vertex).delete_all }   end

This generates exactly the compact SQL that you'd expect, both for finding:

  >> Edge.connected_to(v1)   => SELECT "edges.*" FROM "edges" WHERE (edges.vertex_a_id = 2 OR edges.vertex_b_id = 2)

and for deleting:

  >> Edge.connected_to(v1).delete_all   => DELETE FROM "edges" WHERE (edges.vertex_a_id = 2 OR edges.vertex_b_id = 2)

O Marnen, Marnen! wherefore art thou Marnen?

Fun fact: "wherefore" means "why". [1]

I wonder where Marnen went, but I never wondered why he was Marnen. I did appreciate his unique brand of scorched earth and incredibly precise answers.

Walter

1. In the famous quote, Juliet is wondering *why* Romeo is named the way he is; because of his name, she cannot be allowed to love him. Her logic, typical for Shakespeare women, is quite sound: "A rose by any other name would smell as sweet..."

Walter Davis wrote in post #1016477:

Resurrecting an old thread because it has a useful lesson:

Marnen Laibow-Koser wrote in post #927576:

[snip]

O Marnen, Marnen! wherefore art thou Marnen?

Fun fact: "wherefore" means "why". [1]

Indeed.

I wonder where Marnen went, but I never wondered why he was Marnen. I did appreciate his unique brand of scorched earth and incredibly precise answers.

Um, thanks...? I don't know whether to feel flattered or put down. :smiley:

Anyway, I'm still around. I just haven't been posting much. I went on a self-imposed vacation from posting back in January (because I was getting too upset to respond as nicely as I wanted to!), and never got back into the habit. I'll probably be a more regular contributor again in the near future.

Walter

1. In the famous quote, Juliet is wondering *why* Romeo is named the way he is; because of his name, she cannot be allowed to love him. Her logic, typical for Shakespeare women, is quite sound: "A rose by any other name would smell as sweet..."

Yup.

Best,

I hope you feel flattered, at least that was what I was aiming for. I did very much enjoy your posts, and learned quite a lot from them. I hope you can find a middle path between saint-like "suffer fools gladly" and and "getting too upset" to start answering again. I'm sure you have quite a lot more to teach me, and others here.

Walter