2017-09-16 19 views
1

私は3つの特定のモデルを持つアプリを持っています。ユーザー、国、都市。Rubyモデルの関係 - 三方の関係

ユーザー

  • にhas_many:国
  • にhas_many:都市、経由:それぞれのための理想的な関係は以下のようになり、国

  • has_mどの:ユーザー
  • にhas_many:都市

  • にhas_many:ユーザー
  • belongs_toの:国

それらの関係のほとんどはあるかなりまっすぐ進む、しかし、ユーザーと都市の関係は私に少し問題を与えています。ユーザーが都市に関連付けられている国に割り当てられている限り、ユーザーに都市を割り当てることができるようにしたいと考えています。今私は、ユーザーに都市を割り当てることさえできず、制限のための正しい論理を立てることはできません。

これまで私が読んで検索することができたのは、以下のとおりです。

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+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[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 
+0

'countries_users'と' cities_users'という2つの標準ジョインテーブルを作成し、後者に**モデル検証**を書いて、その都市がユーザに割り当てられていることを表明できますか? –

+0

サイドノート:おそらく、ユーザーがある国から**割り当てられていない**を防止するために、削除前または削除後のロジックを必要とするかもしれませんが、まだ都市に割り当てられたままです。 (つまり、そのような削除を禁止するか、トランザクションの一部として他の関連付けを削除するかのいずれかです。) –

+0

ユーザーはhas_many都市として都市を割り当てることはできません。ユーザは、都市をユーザに割り当てることができるように、所属する必要があります。あなたが本当に望むのは、ユーザーに訪問を割り当て、訪問に都市を割り当てることです。あなたの質問を理にかなっているものに更新し、私がさらに助けることができるかもしれません。あなたの問題を引き起こしているシンプル化をスクラップして、あなたの要求を破るようなことをすることができると思うようにしてください。 – jamesc

答えて

0

は、ユーザーが は市内

することができますに関連する国に割り当てられている限り、ユーザーに街を割り当てることができるようにしたいですあなたもしたいと思いませんか?

国を作成します。都市を作成し、それを国に割り当てます。旅行または訪問を作成し、その訪問または旅行を都市に割り当て、その旅行または訪問にユーザーを追加します。 Voila!あなたは必要な関係を持っています!おもう。

都市belongs_toの都市 訪問 ユーザーhas_manyの訪問 belongs_toの国 belongs_toの都市旅行、ユーザーhas_manyのは をトリップ両方の旅行や訪問は、市内に属しているので、あなたは、両側の関係を通じてhas_manyのを持つことができますしたがって、訪問と旅行のテーブルには市区町村とユーザーIDの両方が必要です あなたは必要な関係と必要な論理を持っています。

都市にユーザーを割り当てる方法がないため、都市にユーザーを直接参加させるなどの要件を守らないようにしないでください。実際には、さらに大きな頭痛を与えるだけでなく、多くの国の多くの都市を多くの訪問に連れて行きたいと思うかもしれません。その場合は、都市ではない旅行に訪問を割り当てる必要があります。

これはコーディングの問題ではなく、論理的な問題です。コンソールで、その後のコメント

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 

に対応して

UPDATE

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都市をしたいですまもなく働くだろう

OK、旅行には多くの都市と都市がありますhas_many trips has_manyはあなたの関係の両側にありますおそらくtrip_city_xrefと呼ばれる新しい相互参照テーブルが必要になります。これはcity_idとtrip_idの複合主キーを持っています

これは私が嫌うhas_and_belongs_to_many関係です。私の意見では、has_and_belongs_to_manyは、Railsが間違っていた唯一のものに関するものでした。それは短絡です。他の人はそれに同意しませんが、これは私がショートカットを持たない言語でこれを設定する方法です。私はそれがより明確であいまいではないと思う。だから私はそうだから、あなたが今、あなたのユーザーに何をしますかhas_and_belongs_to_manyアソシエーションhere をよく読んで行く気軽にちょうど私の意見です。この

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 

のようなものを設定するのでしょうか?繰り返しますが、私は、だからあなたのユーザ・モデルは、ユーザーがあなたが関係を平らにしてマッピングすることができます旅行で訪問しているすべての都市を取得するには、この

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 

ようになり、まもなく

UPDATE User.cities を更新します(:city_trip_xrefs &).flatten.map(&:市)のu = User.first u.trips.mapので よう

ですから、上記のように使用して都市を取得するには、ユーザーのクラスにメソッドを作ることができます上記のユーザーモデルに示されています。とにかく、都市をユーザーに割り当てることについては、実際には直接行う必要はありません。あなたは旅行を設定し、その旅行にユーザーと都市を追加し、あなたの要件が多少異なる場合を除き、実際の例で必要と思われるすべての機能を持っています。

上記の作成されたレコードコンソールプラスワン既存の都市のための都市への多くの旅行に多く参加し、あなたをトリップする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 

次のget最初のユーザーの最初の旅行の最初の都市の名前

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 > 

希望があります。

+0

私は訪問モデルを取り除きました。上記のコードを私があなたが示唆していると思ったものに編集しました。それでもうまくいきません。私は 'user1.cities << [city1]'を割り当てることはできませんが、近くに見えます。 –

+0

もちろんできません。あなたは旅行をする必要があります。私はあなたが本当にあなたがしたいことを理解していると思うわけではありません。私はすぐにあなたが設定したものがうまくいかなければならない方法で私の答えを更新します。しかし、私はまだあなたが望むものだとは思わない。個人的には、UMLの使用例を書いた後、または少なくとも仕様を書いた後でこれに戻ってきました – jamesc