जवाबों:
कई तरह के कई रिश्ते हैं; आपको अपने आप से निम्नलिखित प्रश्न पूछने होंगे:
वह चार अलग-अलग संभावनाएं छोड़ता है। मैं नीचे इन पर चलूँगा।
संदर्भ के लिए: विषय पर रेल प्रलेखन । "कई-से-कई" नामक एक अनुभाग है, और निश्चित रूप से स्वयं क्लास विधियों पर प्रलेखन।
यह कोड में सबसे कॉम्पैक्ट है।
मैं आपके पोस्ट के लिए इस मूल स्कीमा के साथ शुरुआत करूँगा:
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
और वह बस काम करना चाहिए! यहाँ एक उदाहरण irb सेशन चलाया गया है 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संघ के लिए मॉडल का उपयोग नहीं करते हैं । इस कारण से, आप किसी भी अतिरिक्त फ़ील्ड तक नहीं पहुँच पाएंगे।ठीक है, अब ... आपको एक नियमित उपयोगकर्ता मिल गया है जिसने आज आपकी साइट पर पोस्ट किया है कि ईल कैसे स्वादिष्ट होते हैं। यह कुल अजनबी आपकी साइट के आसपास आता है, साइन अप करता है और नियमित उपयोगकर्ता की अयोग्यता पर एक डांट पोस्ट लिखता है। आखिरकार, ईल एक लुप्तप्राय प्रजाति हैं!
तो आप अपने डेटाबेस में स्पष्ट करना चाहते हैं कि पोस्ट बी पोस्ट ए पर डांट-फटकार है। ऐसा करने के लिए, आप 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 पैरामीटर नहीं है।) यह आवश्यक है, क्योंकि टेबल तक पहुंचने के लिए एक नियमित ActiveRecord मॉडल होगा।
मैं 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हम इसका पता नहीं लगा सकते हैं। हमें इसे स्पष्ट रूप से बताना होगा।
अब 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
वाक्यविन्यास में मामूली बदलाव के अलावा, दो वास्तविक चीजें यहां भिन्न हैं:
has_many :post_connectionsएक अतिरिक्त है :dependentपैरामीटर। मान के साथ :destroy, हम रेल को बताते हैं कि, एक बार जब यह पद गायब हो जाता है, तो यह आगे जाकर इन वस्तुओं को नष्ट कर सकता है। एक वैकल्पिक मूल्य जो आप यहां उपयोग कर सकते हैं :delete_all, जो तेज है, लेकिन यदि आप उन का उपयोग कर रहे हैं तो किसी भी नष्ट होने वाले हुक को नहीं कहेंगे।has_manyलिए एक एसोसिएशन जोड़ा है , जो कि हमारे माध्यम से जुड़े हुए हैं । इस तरह, रेल बड़े करीने से उन लोगों को भी नष्ट कर सकता है। ध्यान दें कि हमें यहां निर्दिष्ट करना होगा , क्योंकि मॉडल का वर्ग नाम अब से अनुमान नहीं लगाया जा सकता है ।post_b_id:class_name:reverse_post_connectionsइसके स्थान पर, मैं आपके माध्यम से एक और irb सत्र लाता हूं 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तालिका के साथ वैकल्पिक विधि और मॉडल के लिए भी यही सच है ।
यह सबसे अच्छी तरह से देखा जाता है जब बस irb से संघों तक पहुँचने, और SQL कि रेल लॉग फ़ाइल में उत्पन्न देख रहा है। आपको निम्न जैसा कुछ मिलेगा:
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आदि यह बहुत नहीं है। (मैं यहाँ भी सुझाव के लिए खुला हूँ। कोई भी?)
Shteef द्वारा प्रस्तुत प्रश्न का उत्तर देने के लिए:
उपयोगकर्ताओं के बीच अनुयायी-अनुवर्ती संबंध एक द्वि-दिशात्मक लूपेड एसोसिएशन का एक अच्छा उदाहरण है। एक उपयोगकर्ता कई हो सकते हैं:
यहां बताया गया है कि user.rb के लिए कोड कैसे दिख सकता है:
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
यहाँ कैसे follow.rb के लिए कोड है :
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_followsuser.rb में हैं। एक उदाहरण के रूप में मिल (गैर-लूपेड) एसोसिएशन के एक रन का उपयोग करने के लिए, एक टीम में कई हो सकते हैं: के playersमाध्यम से :contracts। यह एक के लिए कोई अलग है प्लेयर , जो कई हो सकता है :teamsके माध्यम से :contractsऔर साथ ही (जैसे के पाठ्यक्रम पर प्लेयर के कैरियर)। लेकिन इस मामले में, जहां केवल एक नामांकित मॉडल मौजूद है (यानी एक उपयोगकर्ता ), नामकरण के माध्यम से: रिश्ते को पहचान के रूप में (उदाहरण के लिए through: :follow, या, जैसे पोस्ट उदाहरणों में ऊपर किया गया था through: :post_connections) , विभिन्न उपयोग मामलों के लिए नामकरण टकराव में परिणाम होगा ( या प्रवेश तालिका में पहुंच बिंदु)। :follower_followsतथा:followee_followsइस तरह के नामकरण टकराव से बचने के लिए बनाया गया था। अब, एक उपयोगकर्ता कई हो सकता है :followersके माध्यम से :follower_followsऔर कई :followeesके माध्यम से :followee_follows।
उपयोगकर्ता का अनुसरण करने के लिए : अनुयायियों ( @user.followeesडेटाबेस के लिए एक कॉल पर ), रेल अब class_name के प्रत्येक उदाहरण को देख सकते हैं: "का पालन करें" जहां इस तरह के उपयोगकर्ता के foreign_key: :follower_idमाध्यम से अनुयायी (यानी ) है: इस तरह के उपयोगकर्ता : followee_folls। उपयोगकर्ता का अनुसरण करने के लिए : अनुयायियों ( @user.followersडेटाबेस के लिए एक कॉल पर ), रेल अब class_name के प्रत्येक उदाहरण को देख सकते हैं: "का पालन करें" जहां इस तरह के उपयोगकर्ता केforeign_key: :followee_id माध्यम से अनुयायी (यानी ) है: इस तरह के उपयोगकर्ता : follower_follows।
यदि कोई भी यहां यह जानने की कोशिश करने के लिए आया कि रेल में मित्र संबंध कैसे बनाए जाएं, तो मैं उन्हें संदर्भित करता हूं कि मैंने आखिरकार क्या उपयोग करने का फैसला किया है, जो कि 'कम्युनिटी इंजन' ने कॉपी किया है।
आप निम्न का उल्लेख कर सकते हैं:
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"
@ स्टीफन कोचन से प्रेरित, यह द्वि-दिशात्मक संघों के लिए काम कर सकता है
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दोनों को काम करना चाहिए, कम से कम मेरे लिए काम किया।
द्वि-दिशात्मक के लिए belongs_to_and_has_many, पहले से ही पोस्ट किए गए महान जवाब को देखें, और फिर एक अलग नाम के साथ एक और एसोसिएशन बनाएं, विदेशी चाबियाँ उलट गईं और यह सुनिश्चित करें कि आपने class_nameसही मॉडल पर वापस इंगित किया है। चीयर्स।
यदि किसी के पास काम करने के लिए उत्कृष्ट उत्तर पाने के मुद्दे हैं, जैसे:
(ऑब्जेक्ट #inspect का समर्थन नहीं करता है)
=>
या
NoMethodError: अपरिभाषित विधि `विभाजन 'के लिए: मिशन: प्रतीक
तब समाधान के :PostConnectionसाथ प्रतिस्थापित करने के लिए है "PostConnection", पाठ्यक्रम के अपने वर्गनाम को प्रतिस्थापित करना।
:foreign_keyपरhas_many :throughजरूरी नहीं है, और मैं कैसे बहुत आसान उपयोग करने के बारे में एक स्पष्टीकरण जोड़ा:dependentके लिए पैरामीटरhas_many।