如何从两个不同的外键访问模型?
How to access model from two distinct foreign keys?
我有两个相关模型,Trip 和 Ride。 Trip 可能有去程或回程。我的 ActiveRecord 模型如下所示:
class Route < ActiveRecord::Base
has_one :trip
end
class Trip < ActiveRecord::Base
belongs_to :going_route, class_name: "Route"
belongs_to :returning_route, class_name: "Route"
end
然而,当我想从路线访问行程时,这给我带来了一个问题:
Route.first.trip
这将引发 PostgreSQL 错误:
PG::UndefinedColumn: ERROR: column trips.route_id does not exist
我如何告诉 Route
class 他的行程在 going_route_id
或 returning_route_id
之下?或者也许还有其他解决方法?
P.S:我一直在寻找类似的问题,有很多,但是none正是这个,解决了我的问题。如果您有任何关于如何使差异更清晰的提示,尤其是对于标题。 Here 是一些类似的问题
编辑:
我也试过使用 lambda,如 matthew's duplicate proposition:
class FavoriteRoute < ActiveRecord::Base
has_one :favorite_trip, -> (route) { where("going_route_id = :id OR returning_route_id = :id", id: route.id) }
end
这将引发相同的错误。如果我假设我应该使用 find_by
而不是 where
因为我只需要一个结果,我有另一个我真的不明白的错误:
NoMethodError: undefined method `except' for #<Trip:0x00007f827b9d13e0>
您需要在 belongs_to
关联的反面指定外键 - 即引用外键的 has_one
/ has_many
面:
class Trip < ActiveRecord::Base
# specifying foreign_key here is not needed since AR
# will deduce that its outbound_route_id
belongs_to :outbound_route,
class_name: "Route"
belongs_to :return_route,
class_name: "Route"
end
class Route < ActiveRecord::Base
has_one :trip_as_outbound,
class_name: 'Trip',
foreign_key: :outbound_route_id
has_one :trip_as_returning,
class_name: 'Trip',
foreign_key: :return_route_id
def trip
trip_as_outbound || trip_as_returning
end
end
解决此问题的一种方法是使用单一 Table 继承:
class Route < ApplicationRecord
end
class Routes::Outbound < ::Route
self.table_name = 'routes'
has_one :trip, foreign_key: :outbound_route_id
end
class Routes::Return < ::Route
self.table_name = 'routes'
has_one :trip, foreign_key: :return_route_id
end
class Trip < ApplicationRecord
belongs_to :outbound_route,
class_name: '::Routes::Outbound'
belongs_to :return_route,
class_name: '::Routes::Return'
end
它会为您提供正确的行程,但有一些奇怪之处,例如 Routes::Return.all
会为您提供与 Route.all
相同的结果。
这可以通过将 type
字符串列添加到 routes
table 来解决。为了提高性能,在类型和 ID 上添加复合索引。
由于going
和returning
路线在功能上没有区别,考虑在行程和路线之间建立has_many关系。这将使路线可重复用于其他旅行,并为您提供所需的东西。
注意:此方法存在缺陷,因为您使用的是多对多关系。这意味着一次旅行可能有不止一条 and/or 回程路线。您可以通过 Trip 模型中的代码来管理它,或者如果您想为任一方向生成一条 "multi-stop" 路线,这可能还不错。
您将生成一个名为 trip_routes
的模型。
trip_routes
迁移可能如下所示:
create_table :trip_routes do |t|
t.integer :trip_id
t.integer :route_id
t.string :route_type
t.boolean :favorite
end
# Consider this part based on how you think your indexes are best built, I'm
# just making note that DB performance can be impacted particularly on these
# two fields.
add_index :trip_routes, :trip_id
add_index :trip_routes, :route_id
您的 trip_route
模型将如下所示:
class TripRoute < ActiveRecord::Base
belongs_to :trip
belongs_to :route
# This model knows whether it's the 'going' or 'returning' route, so do your
# route functionality here.
end
那么您的 trip
模型将如下所示:
class Trip < ActiveRecord::Base
has_many :trip_routes
has_many :route, through: trip_routes
# Helper to get the going route
def going_trip_route
self.trip_routes.find_by(route_type: "going")
end
# Helper to get the going route
def returning_trip_route
self.trip_routes.find_by(route_type: "returning")
end
end
您的 route
模型将如下所示:
class Route < ActiveRecord::Base
has_many :trip_routes
has_many :trips, through: trip_routes
end
在看到所有可能的解决方案和我们的需求后,我们选择了下一个解决方案:
class Route
def trip
@trip ||= Trip.find_by("going_route_id = :id OR returning_route_id = :id", id: id)
end
end
我认为这不是最好的解决方法,而且感觉很老套。然而,这是最快实现的,没有性能问题。此解决方案的另一个问题是没有 rails 验证。
尝试在路由中添加键 table
add_column :routes, :going_key, :integer
add_column :routes, :returning_key, :integer
然后在您的行程和路线模型中
class Route < ActiveRecord::Base
belongs_to :going_route, foreign_key: :going_key, class_name: Trip
belongs_to :returning_route, foreign_key: :returning_key, class_name: Trip
end
class Trip < ActiveRecord::Base
end
Route.first.going_route
Route.first.returning_route
我有两个相关模型,Trip 和 Ride。 Trip 可能有去程或回程。我的 ActiveRecord 模型如下所示:
class Route < ActiveRecord::Base
has_one :trip
end
class Trip < ActiveRecord::Base
belongs_to :going_route, class_name: "Route"
belongs_to :returning_route, class_name: "Route"
end
然而,当我想从路线访问行程时,这给我带来了一个问题:
Route.first.trip
这将引发 PostgreSQL 错误:
PG::UndefinedColumn: ERROR: column trips.route_id does not exist
我如何告诉 Route
class 他的行程在 going_route_id
或 returning_route_id
之下?或者也许还有其他解决方法?
P.S:我一直在寻找类似的问题,有很多,但是none正是这个,解决了我的问题。如果您有任何关于如何使差异更清晰的提示,尤其是对于标题。 Here 是一些类似的问题
编辑:
我也试过使用 lambda,如 matthew's duplicate proposition:
class FavoriteRoute < ActiveRecord::Base
has_one :favorite_trip, -> (route) { where("going_route_id = :id OR returning_route_id = :id", id: route.id) }
end
这将引发相同的错误。如果我假设我应该使用 find_by
而不是 where
因为我只需要一个结果,我有另一个我真的不明白的错误:
NoMethodError: undefined method `except' for #<Trip:0x00007f827b9d13e0>
您需要在 belongs_to
关联的反面指定外键 - 即引用外键的 has_one
/ has_many
面:
class Trip < ActiveRecord::Base
# specifying foreign_key here is not needed since AR
# will deduce that its outbound_route_id
belongs_to :outbound_route,
class_name: "Route"
belongs_to :return_route,
class_name: "Route"
end
class Route < ActiveRecord::Base
has_one :trip_as_outbound,
class_name: 'Trip',
foreign_key: :outbound_route_id
has_one :trip_as_returning,
class_name: 'Trip',
foreign_key: :return_route_id
def trip
trip_as_outbound || trip_as_returning
end
end
解决此问题的一种方法是使用单一 Table 继承:
class Route < ApplicationRecord
end
class Routes::Outbound < ::Route
self.table_name = 'routes'
has_one :trip, foreign_key: :outbound_route_id
end
class Routes::Return < ::Route
self.table_name = 'routes'
has_one :trip, foreign_key: :return_route_id
end
class Trip < ApplicationRecord
belongs_to :outbound_route,
class_name: '::Routes::Outbound'
belongs_to :return_route,
class_name: '::Routes::Return'
end
它会为您提供正确的行程,但有一些奇怪之处,例如 Routes::Return.all
会为您提供与 Route.all
相同的结果。
这可以通过将 type
字符串列添加到 routes
table 来解决。为了提高性能,在类型和 ID 上添加复合索引。
由于going
和returning
路线在功能上没有区别,考虑在行程和路线之间建立has_many关系。这将使路线可重复用于其他旅行,并为您提供所需的东西。
注意:此方法存在缺陷,因为您使用的是多对多关系。这意味着一次旅行可能有不止一条 and/or 回程路线。您可以通过 Trip 模型中的代码来管理它,或者如果您想为任一方向生成一条 "multi-stop" 路线,这可能还不错。
您将生成一个名为 trip_routes
的模型。
trip_routes
迁移可能如下所示:
create_table :trip_routes do |t|
t.integer :trip_id
t.integer :route_id
t.string :route_type
t.boolean :favorite
end
# Consider this part based on how you think your indexes are best built, I'm
# just making note that DB performance can be impacted particularly on these
# two fields.
add_index :trip_routes, :trip_id
add_index :trip_routes, :route_id
您的 trip_route
模型将如下所示:
class TripRoute < ActiveRecord::Base
belongs_to :trip
belongs_to :route
# This model knows whether it's the 'going' or 'returning' route, so do your
# route functionality here.
end
那么您的 trip
模型将如下所示:
class Trip < ActiveRecord::Base
has_many :trip_routes
has_many :route, through: trip_routes
# Helper to get the going route
def going_trip_route
self.trip_routes.find_by(route_type: "going")
end
# Helper to get the going route
def returning_trip_route
self.trip_routes.find_by(route_type: "returning")
end
end
您的 route
模型将如下所示:
class Route < ActiveRecord::Base
has_many :trip_routes
has_many :trips, through: trip_routes
end
在看到所有可能的解决方案和我们的需求后,我们选择了下一个解决方案:
class Route
def trip
@trip ||= Trip.find_by("going_route_id = :id OR returning_route_id = :id", id: id)
end
end
我认为这不是最好的解决方法,而且感觉很老套。然而,这是最快实现的,没有性能问题。此解决方案的另一个问题是没有 rails 验证。
尝试在路由中添加键 table
add_column :routes, :going_key, :integer add_column :routes, :returning_key, :integer
然后在您的行程和路线模型中
class Route < ActiveRecord::Base
belongs_to :going_route, foreign_key: :going_key, class_name: Trip
belongs_to :returning_route, foreign_key: :returning_key, class_name: Trip
end
class Trip < ActiveRecord::Base
end
Route.first.going_route
Route.first.returning_route