A country has many states, and a state has many cities. I want to ensure that no two cities in the same country have the same name.
class Country < ActiveRecord::Base
has_many :states
has_many :cities, :through => :states
accepts_nested_attributes_for :states, :reject_if => lambda { |state| state[:name].blank?}, :allow_destroy => true
validates_uniqueness_of :name
end
class State < ActiveRecord::Base
belongs_to :country
has_many :cities
accepts_nested_attributes_for :cities, :reject_if => lambda { |city| city[:name].blank?}, :allow_destroy => true
validates_presence_of :country
validates_uniqueness_of :name, :scope => :country_id
end
class City < ActiveRecord::Base
belongs_to :state
has_one :country, :through => :state
validates_presence_of :state
validate :name_is_unique_in_country
private
def name_is_unique_in_country
if City.joins(:state, :country).where(:cities => {:name => name}, :countries => {:id => State.find(state_id).country.id }).any?
error.add("Two cities in the same Country can not have the same name")
end
end
end
Is there a simpler way? 开发者_JAVA技巧I'm not really liking 'name_is_unique_in_country'. The join is a bit messy, and I find myself wishing for something like:
:validates_uniqueness_of :name, :scope => country
But that's not possible as information about the country is only available through the state.
In addition, I'm running into problems when I use nested attributes to create a state and city at the same time...
Personally I would add a country_id
column to cities
, even though that's obviously redundant. I think sometimes it's ok to screw normalization and keep it speedy and straightforward. But that's just me. validates_uniqueness_of
only seems to accept actual table columns in its scope.
精彩评论