我有一个具有3个特定模型的应用;用户,国家和城市。每个人的理想关系如下:
用户
- HAS_MANY:国家
- has_many:城市,通过:: country
country
- has_many:用户
- has_many:城市
城市
- has_many:用户
- 属于:乡村
这些关系中的大多数都很直接,但是,用户到城市的关系给了我一些问题。我希望能够将城市分配给用户,只要用户被分配给与城市相关的国家。现在,我什至无法为用户分配一个城市,更不用说为限制建立正确的逻辑。
到目前为止,我能够阅读和搜索的内容已导致下面。
user.rb
class User < ApplicationRecord
before_save { self.email = email.downcase}
#attr_accessible :username, :email
validates_confirmation_of :password
has_secure_password
validates :username, presence: true, length: { maximum: 25 }, uniqueness: {case_sensitive: false}
VALID_EMAIL_REGEX = /A[w+-.]+@[a-zd-]+(.[a-zd-]+)*.[a-z]+z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
validates :password, presence: true, confirmation: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
has_many :trips
has_many :countries, through: :trips
has_many :cities, through: :trips
end
country.rb
class Country < ApplicationRecord
has_many :trips
has_many :cities
has_many :users, through: :trips
end
city.rb
class City < ApplicationRecord
has_many :trips
belongs_to :country
end
trip.rb
class Trip < ApplicationRecord
belongs_to :country
belongs_to :user
belongs_to :city
end
只要用户拥有 被分配给与城市相关的国家
你不能,也不应该。
创建一个国家。创建一个城市并将其分配给一个国家。创建旅行或访问,分配访问或前往城市的旅行,然后将用户添加到该旅行或访问中。瞧!您有需要的关系!我认为。
一个城市属于一个国家旅行属于城市访问属于城市用户has_many访问用户has_many旅行因为两次旅行和访问都属于一个城市您拥有所需的关系和所需的逻辑。
不要尝试打破您的要求,例如直接加入城市,因为无法将城市分配给用户。的确,只是为了让您更大的头痛,您甚至可能想参加许多国家/地区的许多城市的旅行。在这种情况下,您希望将访问分配到旅行而不是城市。
这是一个逻辑问题,而不是编码问题。
响应评论的更新
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城市,所以我将尽快更新它的工作方式
好吧,要达到下一个旅行有许多城市和城市的下一个阶段,而曼尼的旅行指出,您将需要一个新的交叉参考表的has_many可能称为trip_city_xref,它具有具有复合主键city_id and trip_id
这是在轨道上是我讨厌的has_and_belongs_to_many关系。我认为,has_and_belongs_to_many是唯一误会的事情。这是一个捷径。其他人会不同意,但这就是我用没有那么捷径的语言进行设置的方式。我认为这更清楚,不太模棱两可。所以我会设置这样的东西
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那么,您现在与用户做什么?同样,我很快会更新
更新用户。城市因此您的用户模型看起来像这样
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 = user.firstu.trips.map(&amp;:city_trip_xrefs).flatten.map(&amp;:city)
因此,您可以在用户类上制作一种方法,以使用上述用户模型中所示的上述城市获取城市。至于将城市分配给用户,无论如何您真的不想直接这样做。您将设置旅行,将用户和城市添加到该旅行中,然后在现实世界中拥有所有可能需要的功能,除非您的要求有所不同
使用上述创建的记录在控制台上加上City_trip_xref表中的记录将所有记录放在一起
首先在控制台中创建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 >
希望这是有道理的。