SORU
30 Ocak 2010, CUMARTESİ


Raylar aynı modeli ile çok-çok ilişki?

Nasıl raylar aynı modeli ile çok-çok ilişki yapabilir miyim?

Örneğin, her yazı birçok mesaj bağlanır.

CEVAP
30 Ocak 2010, CUMARTESİ


Çok-çok ilişkileri vardır; kendinize şu soruları sormak gerekir:

  • Dernek ek bilgi depolamak istiyor muyum? (Ek tablo alanlar katılabilir.)
  • Dernekler örtülü olarak iki yönlü olması gerekiyor mu? (Eğer yazı Bir B post bağlıysa, o zaman yazı B de direğe bağlanır A.)

Dört farklı olasılık kalıyor. Bu aşağıda üzerinde yürüyeceğim.

Başvuru için: the Rails documentation on the subject. Bir bölüm var, adı “çok-Çok”, ve tabii ki sınıf yöntemleri belgelerine kendilerini.

Basit bir senaryo, tek yönlü, hiçbir ek alanlar

Bu kodu en kompakt.

Yazılarınız için bu temel şema ile başlarım

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

Çok-çok ilişki için bir masaya ihtiyacın var. İşte bunun için: şema

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

Varsayılan olarak, Raylar bu tablo katılıyoruz iki tablo isimlerini bir arada arar. Ama o post_connections yerine almaya karar verdim, bu durumda posts_posts olarak ortaya çıkmaktadır.

Burada çok önemli id sütun varsayılan atlamak için :id => false,. Raylar bu sütun her yerde istiyorhariçkatılmak tablolar has_and_belongs_to_many için. Yüksek sesle şikayet edecek.

Son olarak, sütun adlarını standart olmayan (post_id), çatışmayı önlemek için dikkat edin.

Şimdi bu model, sadece Raylar standart olmayan şeyler hakkında bu birkaç söylemek gerekir. Aşağıdaki gibi görünecektir

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

Ve bu sadece iş! İşte örnek bir ırb oturum script/console ile çalıştırın:

>> 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 dernek için atama uygun olarak post_connections tablodaki kayıtları yaratacak bulabilirsiniz.

Bazı şeyler için not:

  • Dernek, tek yönlü olduğunu yukarıda ırb oturumda gördüğünüz gibi, ilk yazı içermez a.posts = [b, c], çünkü sonra.
  • Fark etmişsinizdir başka bir şey 27* *hiçbir model yoktur. Normalde has_and_belongs_to_many bir dernek için modeller kullanma. Bu nedenle, herhangi bir ek alanlar erişmek mümkün olmayacaktır.

Tek yönlü, ek alanlar ile

Şimdi... bugün yılan balığı çok lezzetli hakkında sitenizde bir post yapmış düzenli bir kullanıcı var. Yabancı bir adam etrafında site, belirtileri gelir ve normal bir kullanıcı beceriksizliği üzerine azarlar bir yazı yazar. Sonuçta, yılan balıklarının nesli tükeniyor!

Post A. bunu yapmak İçin yazılan B azarlar bir rant veritabanınızdaki açıklığa kavuşturmak istiyorum, Derneği category bir alan eklemek istiyorum.

İhtiyacımız olan şey artık has_and_belongs_to_many amahas_many, belongs_to, has_many ..., :through => ... ve ekstra bir model bir kombinasyon için birleştirme bir tablo. Bu ekstra model Amerikan Derneği kendisi için ek bilgi eklemek için güç verir.

Burada başka bir şema, yukarıda çok benzer:

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

Nasıl, bu durumda, post_connections dikkat edinyokid bir sütun var. (Varhayır:id => false parametre.) Bu tablo erişmek için düzenli ActiveRecord bir model olacak çünkü gerekli.

Ölü basit çünkü PostConnection model ile başlayacağım:

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

Tek şey burada Raylar burada bir görev ile karşı karşıyayız post_a post_b ikna edemiyor çünkü gerekli olan :class_name. Bunu açıkça söylemek zorundayız.

Şimdi Post model:

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 ilk Derneği ile model posts.id = post_connections.post_a_id 46 *katılmak için söylüyoruz.

İkinci dernek, biz anlatmaya Raylar o ulaşabileceğimiz diğer mesajlar, bağlı olanlar için bu, bizim ilk Derneği post_connections ardından post_b Derneği PostConnection.

Sadece oradabir şey daha vareksik ve o PostConnection a ait mesaj bağımlı olan Raylar anlatmalıyız. post_a_id post_b_id biri veya her ikisi de bağlantı pek bir şey olmaz o zaman 54 ** olsaydı, değil mi? Post modelimiz bu işi böyle yapacağız:

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

Sözdizimi ufak bir değişiklik dışında, iki gerçek burada işler farklı

  • has_many :post_connections 58 *fazladan bir parametre vardır. Değeri ile :destroy bu yazı kaybolur sonra, devam edin ve bu nesneleri yok edebilir, bu Raylar söylüyoruz. İşte kullanabileceğiniz alternatif bir değer daha hızlı olan :delete_all, ama eğer varsa o kullanıyorsanız kanca yok.
  • İçin has_many bir dernek eklediktersbağlantıları da, bize bağlı olanlar post_b_id. Bu yol, Raylar düzgün o da yok edebilir. Model sınıf adı artık :reverse_post_connections olayla olabilir çünkü :class_name burada belirtmek zorunda olduğumuzu unutmayın.

Bu, script/console başka bir ırb oturum getirdim:

>> 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

Dernek oluşturma ve daha sonra ayrı ayrı kategori ayarı yerine, sadece bir PostConnection oluşturmak ve bu iş biter

>> 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!">]

Ve aynı zamanda post_connections reverse_post_connections dernekler kullanabiliriz; düzgün posts dernek yansıtacak:

>> 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
=> []

Çift yönlü dernekler döngüye

has_and_belongs_to_many normal dernekler, dernek tanımlanırher ikisi demodelleri dahil. Dernek çift yönlüdür.

Ama bu durumda tek bir Yazı modeli var. Dernek sadece bir kez belirtildi. Bu özel durumda, dernekler, tek yönlü olan tam da bu yüzden.

Aynı has_many ve bir model ile alternatif yöntem tablo katılmak için de geçerlidir.

Bu sadece ırb gelen dernekler erişme ve Raylar günlük dosyası oluşturur SQL bakıldığında en iyi görülür. Aşağıdaki gibi bir şey göreceksiniz:

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

Yapmak dernek çift yönlü, yapmak zorundayız bulmak için bir yol Rayları OR yukarıdaki koşulları post_a_id post_b_id tersine, çok daha kolay olacak bak iki taraftan da.

Ne yazık ki, ben biliyorum ki bunu yapmak için tek yol oldukça hacky. El ile SQL*,* 80 ** 79, vb gibi has_and_belongs_to_many seçenekleri kullanarak belirtmek gerekiyor. Hoş bir şey değil. (Öneriler burada açık da değilim. Kimse?)

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Adam Outler

    Adam Outler

    19 EKİM 2006
  • discokatze

    discokatze

    23 EYLÜL 2009
  • Liz Morgan

    Liz Morgan

    4 Aralık 2011