希望能够为用户分配城市,只要用户有
被分配到与城市相关的国家
你不能也不应该想要。
创建一个国家。创建一个城市并将其分配给一个国家。创建旅行或访问,将访问或旅行分配到城市,然后将用户添加到该旅行或访问。瞧!你有你需要的关系!我想。
一个城市属于一个国家
一次旅行属于一个城市
一次访问属于一个城市
一个用户有_many访问
用户 has_many trips
因为 trips 和 visits 都属于一个城市,所以你可以通过连接两边的关系拥有 has_many,因此 visits 和 trips 表需要城市和用户 ID
而且你有你需要的关系和你需要的逻辑。
不要尝试做违反您要求的事情,例如直接将城市加入用户,因为无法将城市分配给用户。事实上,只是为了让您更加头疼,您甚至可能想要多次访问许多国家的许多城市。在这种情况下,您希望将访问分配给旅行而不是城市。
这是逻辑问题,不是编码问题。
更新响应cmets
class User < ApplicationRecord
has_many :trips
has_many :cities, through: :trips
end
class Trip < ApplicationRecord
belongs_to :user
belongs_to :city
end
class City < ApplicationRecord
has_many :trips
has_many :users, through: :trips
end
然后在控制台中
Loading development environment (Rails 5.1.4)
2.4.0 :001 > u = User.create(name: "Fred")
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Fred"], ["created_at", "2017-09-17 14:24:30.720005"], ["updated_at", "2017-09-17 14:24:30.720005"]]
(16.3ms) commit transaction
=> #<User id: 1, name: "Fred", created_at: "2017-09-17 14:24:30", updated_at: "2017-09-17 14:24:30">
2.4.0 :004 > c = City.create(name: "Leeds")
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "cities" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Leeds"], ["created_at", "2017-09-17 14:26:19.293166"], ["updated_at", "2017-09-17 14:26:19.293166"]]
(33.9ms) commit transaction
=> #<City id: 1, name: "Leeds", created_at: "2017-09-17 14:26:19", updated_at: "2017-09-17 14:26:19">
2.4.0 :005 > t = Trip.create(name: "T1", user: u, city: c)
(0.2ms) begin transaction
SQL (0.5ms) INSERT INTO "trips" ("user_id", "city_id", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["user_id", 1], ["city_id", 1], ["name", "T1"], ["created_at", "2017-09-17 14:28:44.656390"], ["updated_at", "2017-09-17 14:28:44.656390"]]
(26.0ms) commit transaction
=> #<Trip id: 1, user_id: 1, city_id: 1, name: "T1", created_at: "2017-09-17 14:28:44", updated_at: "2017-09-17 14:28:44">
2.4.0 :006 > t.user.name#
=> "Fred"
2.4.0 :007 > t.city.name
=> "Leeds"
2.4.0 :008 > t.name
=> "T1"
这就是正常的 has_many through 的工作方式,但您希望在 trips 模型中使用 has_many 城市,所以我会尽快更新它的工作方式
好的,要实现下一个阶段,其中一个旅行有许多城市,一个城市 has_many trips 请注意关系两侧的 has_many,您将需要一个新的交叉引用表,可能称为 trip_city_xref,其复合主键为city_id 和 trip_id
这在 Rails 中是一种我讨厌的 has_and_belongs_to_many 关系。我只认为 has_and_belongs_to_many 是 Rails 唯一出错的地方。这是一个捷径。其他人会不同意,但这就是我用一种没有那种捷径的语言来设置它的方式。我认为它更清晰,更不模棱两可。所以我会设置这样的东西
class Trip < ApplicationRecord
belongs_to :user
belongs_to: :city_trip_xref and
has_many :cities, through: :city_trip_xref
end
class City < ApplicationRecord
has_many :trips, through: :city_trip_xref
has_many :users, through: :trips
end
class CityTripXref < ApplicationRecord
belongs_to :trip
belongs_to :city
end
这只是我的意见,所以请随时阅读 has_and_belongs_to_many here
那么你现在如何处理你的用户呢?再次,我会尽快更新
更新 User.cities
所以你的用户模型可能看起来像这样
class User < ApplicationRecord
has_many :trips
has_many :city_trip_xrefs, through: :trips
def cities
trips.map(&:city_trip_xrefs).flatten.map(&:city)
end
end
要获取用户在旅途中访问的所有城市,您可以像这样展平和映射关系
u = 用户优先
u.trips.map(&:city_trip_xrefs).flatten.map(&:city)
所以你可以在你的用户类上创建一个方法来使用上面的用户模型来获取城市。至于将城市分配给用户,无论如何您都不会真的想直接这样做。您将设置一次旅行,将用户和城市添加到该旅行中,然后您就拥有了现实世界示例中可能需要的所有功能,除非您的要求有所不同
使用上面在控制台中创建的记录加上 city_trip_xref 表中的记录将所有内容放在一起,以加入一个现有城市的多对多城市旅行和您现在可以进行的旅行
首先在控制台中创建外部参照
ctx = CityTripXref.new
=> #<CityTripXref id: nil, city_id: nil, trip_id: nil, created_at: nil, updated_at: nil>
2.4.0 :008 > ctx.city = City.first
=> #<City id: 1, name: "Leeds", created_at: "2017-09-17 14:26:19", updated_at: "2017-09-17 14:26:19">
2.4.0 :009 > ctx.trip = Trip.first
=> #<Trip id: 1, user_id: 1, city_id: 1, name: "T1", created_at: "2017-09-17 14:28:44", updated_at: "2017-09-17 14:28:44">
2.4.0 :010 > ctx.save
接下来获取第一个用户第一次旅行的第一个城市的名称
2.4.0 :028 > u = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Fred", created_at: "2017-09-17 14:24:30", updated_at: "2017-09-17 14:24:30">
2.4.0 :029 > u.cities.first.name
Trip Load (0.1ms) SELECT "trips".* FROM "trips" WHERE "trips"."user_id" = ? [["user_id", 1]]
CityTripXref Load (0.1ms) SELECT "city_trip_xrefs".* FROM "city_trip_xrefs" WHERE "city_trip_xrefs"."trip_id" = ? [["trip_id", 1]]
City Load (0.1ms) SELECT "cities".* FROM "cities" WHERE "cities"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "Leeds"
2.4.0 :030 >
希望这是有道理的。