kjs3
kjs3

Reputation: 6178

Removing the :has_many when the :belongs_to is updated/destroyed if the :has_many is now empty

I have states who have many cities (belongs_to :state) who have many businesses (belongs_to :city). State also… has_many :businesses, :through => :cities

On my site everything is managed from the Business perspective. When a new Business is created/updated the state/city is created if it doesn't already exist. This happens in a :before_save call.

I'm having problems removing States/Cites when a Business gets updated. If the state/city that a business is in gets changed (again this happens from an edit business form) and the old state/city no longer has any businesses I want to destroy it. I've tried doing this in after_save calls but they're wrapped in a transaction and even if I assign variables to the names of the old state/city, they seem to get changed to the new state/city sometime during the transaction. It's crazy! I used "puts" calls to print the vars in some spots in my Business model and watched the vars change during a save. It was frustrating.

So, right now I'm handling this from the controller but it feels hackish.

Here's some of my code.

http://pastie.org/648832

Also, I'd love any input on how better to structure this whole thing.

Thanks

Upvotes: 0

Views: 285

Answers (1)

EmFi
EmFi

Reputation: 23450

You want after_destroy callbacks to destroy the has many side of a relationship if it has none.

To ensure this behaviour after an update, we need to use the ActiveRecord::Dirty methods. Which are built into rails as of 2.1. If you're running an older version you'll need the Dirty plugin

class Business < ActiveRecord::Base
  ...
  after_update :destroy_empty_city
  after_destroy :destroy_empty_city

  protected
    def destroy_empty_city
      c = city_changed? ? city_was : city
      c.destroy if c.businesses.empty?
    end


end

class City < ActiveRecord::Base
  ...
  after_destroy :destroy_empty_state

  protected
    def destroy_empty_state
      state.destroy if state.businesses.empty?
    end


end

You might need to check if city/state.businesses == [self] instead of city/state.businesses.empty? if your associations are eager loaded. I can't remember how rails treats associations after destroy. I'm assuming that if they're eager loaded than the code above won't work and you will need the alternate check. Otherwise it should be fine.

Upvotes: 1

Related Questions