একই মডেলের সাথে রেলের অনেকগুলি সম্পর্ক রয়েছে?


107

আমি কীভাবে একই মডেলের সাথে রেলগুলিতে একাধিক থেকে বেশি সম্পর্ক তৈরি করতে পারি?

উদাহরণস্বরূপ, প্রতিটি পোস্ট অনেকগুলি পোস্টের সাথে সংযুক্ত থাকে।

উত্তর:


276

বিভিন্ন ধরণের বহু রকমের সম্পর্ক রয়েছে; আপনাকে নিম্নলিখিত প্রশ্নগুলি নিজেকে জিজ্ঞাসা করতে হবে:

  • আমি কি সমিতির সাথে অতিরিক্ত তথ্য সঞ্চয় করতে চাই? (যোগদানের টেবিলের অতিরিক্ত ক্ষেত্রগুলি))
  • সমিতিগুলি কি স্পষ্টভাবে দ্বি-দিকনির্দেশক হওয়া দরকার? (পোস্ট এ যদি পোস্ট বি এর সাথে সংযুক্ত থাকে, তবে পোস্ট বি পোস্ট এ এর ​​সাথেও সংযুক্ত থাকে)

এটি চারটি বিভিন্ন সম্ভাবনা ছেড়ে দেয়। আমি নীচে এই উপর দিয়ে হাঁটা করব।

রেফারেন্সের জন্য: বিষয়টিতে রেল ডকুমেন্টেশন । এখানে "অনেকগুলি থেকে অনেকগুলি" নামে একটি বিভাগ রয়েছে এবং অবশ্যই তারা নিজেরাই ক্লাসের পদ্ধতিতে ডকুমেন্টেশন।

সরল দৃশ্য, একক দিকনির্দেশক, কোনও অতিরিক্ত ক্ষেত্র নেই

এটি কোডের মধ্যে সবচেয়ে কমপ্যাক্ট।

আমি আপনার পোস্টগুলির জন্য এই প্রাথমিক স্কিমাটি দিয়ে শুরু করব:

create_table "posts", :force => true do |t|
  t.string  "name", :null => false
end

যে কোনও বহু-বহু সম্পর্কের জন্য আপনার একটি যোগদানের টেবিলের প্রয়োজন। এখানে তার জন্য স্কিমাটি এখানে:

create_table "post_connections", :force => true, :id => false do |t|
  t.integer "post_a_id", :null => false
  t.integer "post_b_id", :null => false
end

ডিফল্টরূপে, রেলগুলি এই টেবিলটিকে আমরা যে দুটি টেবিলে যোগদান করছি তার নামের সংমিশ্রণটি বলবে। তবে এটি posts_postsএই পরিস্থিতিতে যেমন পরিণত হবে , তাই আমি post_connectionsপরিবর্তে নেওয়ার সিদ্ধান্ত নিয়েছি ।

এখানে খুব গুরুত্বপূর্ণ :id => false, ডিফল্ট idকলাম বাদ দিতে । এর জন্য টেবিলগুলিতে যোগ ব্যতীত রেলগুলি সেই কলামটি সর্বত্র চায় has_and_belongs_to_many। এটি উচ্চস্বরে অভিযোগ করবে।

পরিশেষে, লক্ষ্য করুন যে কলামের নামগুলি post_idদ্বন্দ্ব রোধ করার জন্য অ-মানক (পাশাপাশি ) নয় ।

এখন আপনার মডেলটিতে, আপনাকে কেবলমাত্র এই দু'টি অ-মানক জিনিস সম্পর্কে রেলগুলি জানাতে হবে। এটি নীচের মত দেখতে হবে:

class Post < ActiveRecord::Base
  has_and_belongs_to_many(:posts,
    :join_table => "post_connections",
    :foreign_key => "post_a_id",
    :association_foreign_key => "post_b_id")
end

এবং যে সহজভাবে কাজ করা উচিত! এখানে একটি উদাহরণ আইআরবি সেশন চালানো হয়েছে script/console:

>> a = Post.create :name => 'First post!'
=> #<Post id: 1, name: "First post!">
>> b = Post.create :name => 'Second post?'
=> #<Post id: 2, name: "Second post?">
>> c = Post.create :name => 'Definitely the third post.'
=> #<Post id: 3, name: "Definitely the third post.">
>> a.posts = [b, c]
=> [#<Post id: 2, name: "Second post?">, #<Post id: 3, name: "Definitely the third post.">]
>> b.posts
=> []
>> b.posts = [a]
=> [#<Post id: 1, name: "First post!">]

আপনি দেখতে পাবেন যে সমিতিকে বরাদ্দ করা যথাযথভাবে সারণীতে postsরেকর্ড তৈরি করবে post_connections

কিছু বিষয় লক্ষণীয়:

  • আপনি উপরের আইআরবি সেশনে দেখতে পাচ্ছেন যে সমিতিটি এক-দিকনির্দেশক, কারণ এর পরে a.posts = [b, c], আউটপুটটি b.postsপ্রথম পোস্টকে অন্তর্ভুক্ত করে না।
  • আর একটি জিনিস যা আপনি লক্ষ্য করেছেন তা হ'ল কোনও মডেল নেই PostConnection। আপনি সাধারণত কোনও has_and_belongs_to_manyসমিতির জন্য মডেল ব্যবহার করেন না । এই কারণে, আপনি কোনও অতিরিক্ত ক্ষেত্র অ্যাক্সেস করতে পারবেন না।

অতিরিক্ত ক্ষেত্র সহ ইউনি-দিকনির্দেশক with

ঠিক এখনই ... আপনি নিয়মিত ব্যবহারকারীর পেলেন যিনি আজ আপনার সাইটে কীভাবে সুস্বাদু তা সম্পর্কে আপনার পোস্টে একটি পোস্ট করেছেন। এই মোট অচেনা লোকটি আপনার সাইটে প্রায় আসে, সাইন আপ করে এবং নিয়মিত ব্যবহারকারীর অদক্ষতা নিয়ে একটি বদনাম পোস্ট লেখেন। সর্বোপরি, elsল একটি বিপন্ন প্রজাতি!

সুতরাং আপনি আপনার ডাটাবেসে পরিষ্কার করে দিতে চাই যে পোস্ট বি পোস্টের জন্য একটি বদনাম রেন্ট that এটি করার জন্য, আপনি সমিতিতে একটি categoryক্ষেত্র যুক্ত করতে চান ।

আমরা কি প্রয়োজন আর একটি নেই has_and_belongs_to_many, কিন্তু একটি সমন্বয় has_many, belongs_to, has_many ..., :through => ...এবং জন্য একটি অতিরিক্ত মডেল টেবিল যোগদান করুন। এই অতিরিক্ত মডেলটিই আমাদের অ্যাসোসিয়েশনে অতিরিক্ত তথ্য যুক্ত করার শক্তি দেয়।

এখানে আরও একটি স্কিমা রয়েছে, যা উপরোক্তর সাথে খুব সমান:

create_table "posts", :force => true do |t|
  t.string  "name", :null => false
end

create_table "post_connections", :force => true do |t|
  t.integer "post_a_id", :null => false
  t.integer "post_b_id", :null => false
  t.string  "category"
end

লক্ষ্য করুন কিভাবে, এই পরিস্থিতিতে, post_connections আছে একটি আছে idকলাম। ( কোনও :id => false প্যারামিটার নেই )) এটি প্রয়োজনীয়, কারণ টেবিলটি অ্যাক্সেস করার জন্য নিয়মিত অ্যাক্টিভেকর্ড মডেল থাকবে।

আমি PostConnectionমডেলটি দিয়ে শুরু করব , কারণ এটি মৃত সহজ:

class PostConnection < ActiveRecord::Base
  belongs_to :post_a, :class_name => :Post
  belongs_to :post_b, :class_name => :Post
end

এখানে কেবল একটি জিনিস চলছে :class_nameযা প্রয়োজনীয়, কারণ রেলগুলি অনুমান করতে পারে না post_aবা post_bআমরা এখানে কোনও পোস্টের সাথে কাজ করছি dealing আমাদের এটি স্পষ্ট করে বলতে হবে।

এখন Postমডেল:

class Post < ActiveRecord::Base
  has_many :post_connections, :foreign_key => :post_a_id
  has_many :posts, :through => :post_connections, :source => :post_b
end

প্রথম has_manyসমিতি, আমরা যোগদানের জন্য মডেল বলতে post_connectionsউপর posts.id = post_connections.post_a_id

দ্বিতীয় সমিতি, আমরা পাগল বলছেন যে আমরা অন্যান্য পোস্ট পৌঁছতে পারে, এই এক সাথে সংযুক্ত বেশী, আমাদের প্রথম সমিতি মাধ্যমে post_connections, দ্বারা অনুসরণ post_bসমিতি PostConnection

এখানে আরও একটি জিনিস নেই, এবং তা হ'ল আমাদের রেলগুলিকে জানাতে হবে যে এটি যে PostConnectionপোস্টের উপর নির্ভরশীল। এক বা উভয় এর যদি post_a_idএবং post_b_idছিল NULL, তাহলে সেই সংযোগ আমাদের অনেক বলতাম না, এটা কি? আমাদের Postমডেলটিতে আমরা এটি কীভাবে করব তা এখানে :

class Post < ActiveRecord::Base
  has_many(:post_connections, :foreign_key => :post_a_id, :dependent => :destroy)
  has_many(:reverse_post_connections, :class_name => :PostConnection,
      :foreign_key => :post_b_id, :dependent => :destroy)

  has_many :posts, :through => :post_connections, :source => :post_b
end

বাক্য গঠনতে সামান্য পরিবর্তন ছাড়াও দুটি বাস্তব জিনিস এখানে আলাদা different

  • has_many :post_connectionsএকটি অতিরিক্ত আছে :dependentপ্যারামিটার। মান সহ :destroy, আমরা রেলগুলিকে বলি যে, একবার এই পোস্টটি অদৃশ্য হয়ে গেলে, এটি এগিয়ে যেতে পারে এবং এই জিনিসগুলি ধ্বংস করতে পারে। আপনি যে বিকল্প মানটি এখানে ব্যবহার করতে পারেন তা হ'ল :delete_allএটি দ্রুত, তবে যদি আপনি সেগুলি ব্যবহার করেন তবে কোনও ধ্বংসকারী হুককে কল করবেন না।
  • বিপরীত সংযোগগুলির has_manyজন্য আমরা একটি সমিতিও যুক্ত করেছি , যেগুলি আমাদের মাধ্যমে সংযুক্ত হয়েছে post_b_id। এইভাবে, রেলগুলি সেগুলিকে খুব সুন্দরভাবে ধ্বংস করতে পারে। নোট করুন যে আমাদের :class_nameএখানে উল্লেখ করতে হবে , কারণ মডেলের শ্রেণীর নামটি আর থেকে অনুমান করা যায় না :reverse_post_connections

এটি জায়গায় রেখে, আমি আপনার মাধ্যমে আরেকটি আইআরবি সেশন আনি script/console:

>> a = Post.create :name => 'Eels are delicious!'
=> #<Post id: 16, name: "Eels are delicious!">
>> b = Post.create :name => 'You insensitive cloth!'
=> #<Post id: 17, name: "You insensitive cloth!">
>> b.posts = [a]
=> [#<Post id: 16, name: "Eels are delicious!">]
>> b.post_connections
=> [#<PostConnection id: 3, post_a_id: 17, post_b_id: 16, category: nil>]
>> connection = b.post_connections[0]
=> #<PostConnection id: 3, post_a_id: 17, post_b_id: 16, category: nil>
>> connection.category = "scolding"
=> "scolding"
>> connection.save!
=> true

সমিতি তৈরি করার পরে এবং বিভাগটি আলাদাভাবে সেট করার পরিবর্তে, আপনি কেবল একটি পোস্ট সংযোগ তৈরি করতে পারেন এবং এটি দিয়ে সম্পন্ন করতে পারেন:

>> b.posts = []
=> []
>> PostConnection.create(
?>   :post_a => b, :post_b => a,
?>   :category => "scolding"
>> )
=> #<PostConnection id: 5, post_a_id: 17, post_b_id: 16, category: "scolding">
>> b.posts(true)  # 'true' means force a reload
=> [#<Post id: 16, name: "Eels are delicious!">]

এবং আমরা নিপূণভাবে করতে post_connectionsএবং reverse_post_connectionsসমিতির; এটি সংঘে ঝরঝরে প্রতিফলিত হবে posts:

>> a.reverse_post_connections
=> #<PostConnection id: 5, post_a_id: 17, post_b_id: 16, category: "scolding">
>> a.reverse_post_connections = []
=> []
>> b.posts(true)  # 'true' means force a reload
=> []

দ্বি-দিকীয় লুপযুক্ত সমিতিগুলি

সাধারণ has_and_belongs_to_manyঅ্যাসোসিয়েশনে, সমিতি জড়িত উভয় মডেলের সংজ্ঞা দেওয়া হয় । এবং সমিতি দ্বি-দিকনির্দেশক।

তবে এক্ষেত্রে কেবলমাত্র একটি পোস্ট মডেল রয়েছে। এবং সমিতি কেবল একবার নির্দিষ্ট করা হয়। ঠিক এই কারণেই এই নির্দিষ্ট ক্ষেত্রে, সমিতিগুলি এক-দিকনির্দেশক।

বিকল্প পদ্ধতির সাথে has_manyএবং যোগ টেবিলের জন্য একটি মডেলের ক্ষেত্রেও এটি একই।

আইআরবি থেকে সহজেই অ্যাসোসিয়েশনগুলি অ্যাক্সেস করার সময় এবং এসএকিউএল-এর দিকে তাকানো যখন লগ ফাইলে রেলগুলি উৎপন্ন করে এটি সর্বাধিক দেখা যায়। আপনি নিম্নলিখিত মত কিছু পাবেন:

SELECT * FROM "posts"
INNER JOIN "post_connections" ON "posts".id = "post_connections".post_b_id
WHERE ("post_connections".post_a_id = 1 )

অ্যাসোসিয়েশনটিকে দ্বি-দিকনির্দেশক করে তোলার জন্য, আমাদের ORউপরের শর্তাদি post_a_idএবং post_b_idবিপরীতগুলি রেলগুলি তৈরি করার জন্য আমাদের একটি উপায় খুঁজে বের করতে হবে, সুতরাং এটি উভয় দিকেই দেখবে।

দুর্ভাগ্যক্রমে, আমি জানি যে এটি করার একমাত্র উপায় বরং হ্যাকি। আপনি নিজে আপনার এসকিউএল বিকল্পগুলি ব্যবহার করে উল্লেখ করতে হবে has_and_belongs_to_manyযেমন :finder_sql, :delete_sqlইত্যাদি এটি বেশ নয়। (আমি এখানে পরামর্শের জন্য উন্মুক্ত। কেউ?)


সুন্দর মন্তব্যের জন্য ধন্যবাদ! :) আমি আরও কিছু সম্পাদনা করেছি। বিশেষ করে, :foreign_keyউপর has_many :throughপ্রয়োজন নেই, এবং আমি কিভাবে খুব কুশলী ব্যবহার করার জন্য একটি ব্যাখ্যা যোগ :dependentজন্য প্যারামিটার has_many
স্টাফান কোচেন

@ Shtééf এমনকি ভর অ্যাসাইনমেন্ট (আপডেট_ট্রিবিউটস) দ্বি-দিকনির্দেশক সমিতিগুলির ক্ষেত্রেও কাজ করবে না যেমন: postA.update_attributes ({: post_b_ids => [২,৩,৪]}) কোন ধারণা বা কাজের ক্ষেত্র?
লোহিত এমভি

খুব সুন্দর উত্তর সাথী 5. টাইমস + "+1"} রাখে
রাহুল

@ শ্টেফ এই উত্তর থেকে আমি অনেক কিছু শিখেছি, ধন্যবাদ! : আমি জিজ্ঞাসা করুন এবং এখানে আপনার দ্বি-মুখী সমিতি প্রশ্নের উত্তর দিতে চেষ্টা stackoverflow.com/questions/25493368/...
jbmilgrom

17

শিটিফের দ্বারা উত্থাপিত প্রশ্নের উত্তর দিতে:

দ্বি-দিকীয় লুপযুক্ত সমিতিগুলি

ব্যবহারকারীদের মধ্যে অনুসরণকারী-অনুসরণকারী সম্পর্ক দ্বি-দিকনির্দেশক লুপযুক্ত সংঘের একটি ভাল উদাহরণ। একজন ব্যবহারকারীর অনেকগুলি থাকতে পারে:

  • অনুসরণকারী হিসাবে তার ক্ষমতা অনুগামীদের
  • অনুসরণকারী হিসাবে তার ক্ষমতা মধ্যে অনুগামী।

এখানে ব্যবহারকারীর জন্য কোডটি কীভাবে দেখাবে:

class User < ActiveRecord::Base
  # follower_follows "names" the Follow join table for accessing through the follower association
  has_many :follower_follows, foreign_key: :followee_id, class_name: "Follow" 
  # source: :follower matches with the belong_to :follower identification in the Follow model 
  has_many :followers, through: :follower_follows, source: :follower

  # followee_follows "names" the Follow join table for accessing through the followee association
  has_many :followee_follows, foreign_key: :follower_id, class_name: "Follow"    
  # source: :followee matches with the belong_to :followee identification in the Follow model   
  has_many :followees, through: :followee_follows, source: :followee
end

ফলোয়ার.আরবি এর জন্য কোডটি এখানে রয়েছে :

class Follow < ActiveRecord::Base
  belongs_to :follower, foreign_key: "follower_id", class_name: "User"
  belongs_to :followee, foreign_key: "followee_id", class_name: "User"
end

সর্বাধিক গুরুত্বপূর্ণ বিষয়গুলি লক্ষণীয় হ'ল সম্ভবত শর্তাদি :follower_followsএবং :followee_followsইউজারআরবি। মিলের একটি রান (অ-লুপযুক্ত) সমিতি ব্যবহারের উদাহরণ হিসাবে, একটি দলে অনেকগুলি থাকতে পারে: এর playersমাধ্যমে :contracts। এই জন্য কোন আলাদা প্লেয়ার , যারা অনেক থাকতে পারে :teamsমাধ্যমে :contractsপাশাপাশি (যেমন কোর্সের উপর প্লেয়ার এর কর্মজীবন)। তবে এই ক্ষেত্রে, যেখানে কেবলমাত্র একটি নামযুক্ত মডেল উপস্থিত রয়েছে (অর্থাত্ একটি ব্যবহারকারী ), নামকরণের মাধ্যমে: সম্পর্কের through: :followমতো করে (যেমন পোস্টগুলিতে উদাহরণস্বরূপ, বা যেমন লাইকটি সম্পন্ন করা হয়েছিল through: :post_connections) এর বিভিন্ন ব্যবহারের ক্ষেত্রে নামকরণের সংঘর্ষ ঘটবে ( বা এতে প্রবেশের টেবিলগুলিতে প্রবেশ করুন)। :follower_followsএবং:followee_followsএই জাতীয় নামকরণের সংঘাত এড়াতে তৈরি করা হয়েছিল। এখন, একটি ব্যবহারকারী অনেক থাকতে পারে :followersমাধ্যমে :follower_followsএবং অনেক :followeesমাধ্যমে :followee_follows

কোনও ব্যবহারকারীর নির্ধারিত : অনুসরণকারীরা ( @user.followeesডাটাবেসে কল দেওয়ার পরে) ক্লাসের নামগুলি প্রতিটি ক্ষেত্রে দেখতে পাবেন: "অনুসরণ করুন" যেখানে এই জাতীয় ব্যবহারকারী অনুসরণকারী (যেমন foreign_key: :follower_id) এর মাধ্যমে: এই জাতীয় ব্যবহারকারীর : অনুসরণকারী_পাঠগুলি। কোনও ব্যবহারকারীর নির্ধারক : অনুসারীরা ( @user.followersডাটাবেসে একটি কল করার পরে), এখনই শ্রেণি_নামের প্রতিটি উদাহরণটি দেখতে পাবেন: "অনুসরণ করুন" যেখানে এই জাতীয় ব্যবহারকারীর অনুসরণকারী (যেমন foreign_key: :followee_id) এর মাধ্যমে: এই জাতীয় ব্যবহারকারীর : অনুসরণকারী_পথগুলি।


1
ঠিক আমার কী দরকার! ধন্যবাদ! (আমি ডেটাবেস স্থানান্তর তালিকাভুক্ত করারও পরামর্শ দিই; গ্রহণযোগ্য উত্তর থেকে আমাকে সেই তথ্য সংগ্রহ করতে হয়েছিল)
অ্যাডাম ডেনুন

6

কেউ যদি এখানে রেলগুলিতে কীভাবে বন্ধুত্বের সম্পর্ক তৈরি করতে হয় তা জানার চেষ্টা করতে এসেছিলেন, তবে আমি তাদের শেষ পর্যন্ত কী ব্যবহার করার সিদ্ধান্ত নিয়েছিলাম তা সম্পর্কে উল্লেখ করব, যা 'সম্প্রদায় ইঞ্জিন' কী করেছে তা অনুলিপি করে।

আপনি উল্লেখ করতে পারেন:

https://github.com/bborn/communityengine/blob/master/app/models/friendship.rb

এবং

https://github.com/bborn/communityengine/blob/master/app/models/user.rb

আরও তথ্যের জন্য.

টি এল; ডিআর

# user.rb
has_many :friendships, :foreign_key => "user_id", :dependent => :destroy
has_many :occurances_as_friend, :class_name => "Friendship", :foreign_key => "friend_id", :dependent => :destroy

..

# friendship.rb
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"

2

@ স্টাফান কোচেন দ্বারা অনুপ্রাণিত, এটি দ্বি-দিকনির্দেশক সমিতিগুলির জন্য কাজ করতে পারে

class Post < ActiveRecord::Base
  has_and_belongs_to_many(:posts,
    :join_table => "post_connections",
    :foreign_key => "post_a_id",
    :association_foreign_key => "post_b_id")

  has_and_belongs_to_many(:reversed_posts,
    :class_name => Post,
    :join_table => "post_connections",
    :foreign_key => "post_b_id",
    :association_foreign_key => "post_a_id")
 end

তাহলে post.posts&& post.reversed_postsউভয়েরই কাজ করা উচিত, কমপক্ষে আমার পক্ষে কাজ করা উচিত।


1

দ্বি-দিকনির্দেশের জন্য belongs_to_and_has_many, ইতিমধ্যে পোস্ট করা দুর্দান্ত উত্তরটি দেখুন এবং তারপরে একটি আলাদা নামের সাথে অন্য একটি সমিতি তৈরি করুন, বিদেশী কীগুলি বিপরীত হয়েছে এবং আপনি class_nameসঠিক মডেলের দিকে ফিরে এসেছেন কিনা তা নিশ্চিত করুন । চিয়ার্স।


2
আপনি কি আপনার পোস্টে একটি উদাহরণ দেখাতে পারেন? আপনার পরামর্শ অনুসারে আমি একাধিক উপায়ে চেষ্টা করেছি, তবে পেরেক বলে মনে হচ্ছে না।
achabacha322

0

কারও কাছে কাজের দুর্দান্ত উত্তর পেতে সমস্যা থাকলে যেমন:

(অবজেক্ট # ইনস্পেক্ট সমর্থন করে না)
=>

অথবা

NoMethodError: অপরিবর্তিত পদ্ধতি `বিভক্ত 'এর জন্য: মিশন: প্রতীক

তারপরে সমাধানটি হ'ল প্রতিস্থাপন :PostConnectionকরা উচিত "PostConnection", অবশ্যই আপনার ক্লাসের নামটি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.