如何使用活动记录正确定义此关联?
How to define this association properly using active record?
我在一个应用中有以下两个模型
class City
end
class Suburb
end
一个城市恰好有北、东、南、西、中5个郊区。我想通过相应的方法引用每个郊区,这样,
通过 city.north 访问北方
通过 city.south
访问南
我可以为 cities
table 中的每个郊区添加一个外键,并使用 belongs_to
和 has_one
来定义每个关联。但我发现它并不像它应该的那样直观。这是因为 Suburb belongs_to a City 而不是相反。所以下面的定义并不直观。
class City
belongs_to :north, class_name: 'Suburb'
belongs_to :east, class_name: 'Suburb'
belongs_to :south, class_name: 'Suburb'
belongs_to :west, class_name: 'Suburb'
belongs_to :center, class_name: 'Suburb'
end
class Suburb
has_one :city
end
这按预期工作。但是当你阅读它时,它恰恰相反。郊区 belongs_to 城市和城市 has_one :north, has_one :east, has_one :south, has_one :west and has_one :center .
我还尝试在 city
模型上定义一个 has_many :suburbs
,并向郊区模型添加一个枚举 属性 direction
而不是定义一个方法,使用 define_method' 每个方向,但我发现它设计过度了。
有没有办法正确建模。
我会说你需要一个额外的模型,CitySuburb,这样 City 和 Suburb has_many CitySuburb,CitySuburb 同时属于 City 和 Suburb。
您可以在 CitySuburb 上放置北、南等范围,从而将 City 与 north_suburb、south_suburb 等相关联
您的架构本身没有任何问题,但为了便于讨论,让我提出一个满足您的建模问题的替代方案:
按照您的提议,让 Suburb
属于 City
。为了加强郊区相对于城市的唯一性,我们在 suburbs
table 中添加了一个 direction
列,以及一个结合了 city_id
和direction
。这样一个Suburb
正好属于一个城市,一个城市在给定的direction
.
中不能有超过一个Suburb
db/migrate/...create_suburbs.rb
class CreateDeviseUsers < ActiveRecord::Migration
def self.change
create_table(:suburbs) do |t|
# no need for `index: true` here because of composite key below
t.references :city, null: false
t.text :direction, null: false
t.index [:city_id, :direction], unique: true
end
end
end
app/models/city.rb
class City < ActiveRecord::Base
has_many :suburbs
end
我们的 Suburb
模型现在有点复杂。我们需要 direction
的验证以及每个可能值的范围。我喜欢添加一个 getter 以确保 direction
也始终是一个符号。
app/models/suburb.rb
class Suburb < ActiveRecord::Base
DIRECTIONS = [:north, :east, :south, :west]
belongs_to :city
validates :city, presence: true
validates :direction, inclusion: { in: DIRECTIONS }
# define a scope for each direction
DIRECTIONS.each { |d| scope d, -> { where(direction: d) } }
# convenience getter so that we can reason about direction using symbols
def direction
self[:direction].try(:to_sym)
end
end
现在我们可以使用以下范围访问和深入城市郊区:
# all north suburbs
north_suburbs = Suburb.north
# northern suburbs of a city (there can only be one!)
north_suburbs = city.suburbs.north
# as a model
north_suburb = city.suburbs.north.first
如果你实在不喜欢first
位,你可以定义方便访问器:
app/models/city.rb
class City < ActiveRecord::Base
has_many :suburbs
def north
suburbs.north.first
end
# ...
end
我在一个应用中有以下两个模型
class City
end
class Suburb
end
一个城市恰好有北、东、南、西、中5个郊区。我想通过相应的方法引用每个郊区,这样, 通过 city.north 访问北方 通过 city.south
访问南我可以为 cities
table 中的每个郊区添加一个外键,并使用 belongs_to
和 has_one
来定义每个关联。但我发现它并不像它应该的那样直观。这是因为 Suburb belongs_to a City 而不是相反。所以下面的定义并不直观。
class City
belongs_to :north, class_name: 'Suburb'
belongs_to :east, class_name: 'Suburb'
belongs_to :south, class_name: 'Suburb'
belongs_to :west, class_name: 'Suburb'
belongs_to :center, class_name: 'Suburb'
end
class Suburb
has_one :city
end
这按预期工作。但是当你阅读它时,它恰恰相反。郊区 belongs_to 城市和城市 has_one :north, has_one :east, has_one :south, has_one :west and has_one :center .
我还尝试在 city
模型上定义一个 has_many :suburbs
,并向郊区模型添加一个枚举 属性 direction
而不是定义一个方法,使用 define_method' 每个方向,但我发现它设计过度了。
有没有办法正确建模。
我会说你需要一个额外的模型,CitySuburb,这样 City 和 Suburb has_many CitySuburb,CitySuburb 同时属于 City 和 Suburb。
您可以在 CitySuburb 上放置北、南等范围,从而将 City 与 north_suburb、south_suburb 等相关联
您的架构本身没有任何问题,但为了便于讨论,让我提出一个满足您的建模问题的替代方案:
按照您的提议,让 Suburb
属于 City
。为了加强郊区相对于城市的唯一性,我们在 suburbs
table 中添加了一个 direction
列,以及一个结合了 city_id
和direction
。这样一个Suburb
正好属于一个城市,一个城市在给定的direction
.
Suburb
db/migrate/...create_suburbs.rb
class CreateDeviseUsers < ActiveRecord::Migration
def self.change
create_table(:suburbs) do |t|
# no need for `index: true` here because of composite key below
t.references :city, null: false
t.text :direction, null: false
t.index [:city_id, :direction], unique: true
end
end
end
app/models/city.rb
class City < ActiveRecord::Base
has_many :suburbs
end
我们的 Suburb
模型现在有点复杂。我们需要 direction
的验证以及每个可能值的范围。我喜欢添加一个 getter 以确保 direction
也始终是一个符号。
app/models/suburb.rb
class Suburb < ActiveRecord::Base
DIRECTIONS = [:north, :east, :south, :west]
belongs_to :city
validates :city, presence: true
validates :direction, inclusion: { in: DIRECTIONS }
# define a scope for each direction
DIRECTIONS.each { |d| scope d, -> { where(direction: d) } }
# convenience getter so that we can reason about direction using symbols
def direction
self[:direction].try(:to_sym)
end
end
现在我们可以使用以下范围访问和深入城市郊区:
# all north suburbs
north_suburbs = Suburb.north
# northern suburbs of a city (there can only be one!)
north_suburbs = city.suburbs.north
# as a model
north_suburb = city.suburbs.north.first
如果你实在不喜欢first
位,你可以定义方便访问器:
app/models/city.rb
class City < ActiveRecord::Base
has_many :suburbs
def north
suburbs.north.first
end
# ...
end