5

Rails uygulamasının Rails 4.2'ye taşınmasıyla karmaşık has_many ilişkileri. Bu Rails uygulaması kısmen (örneğin JOINs yerine subselects) DB optimizasyonları nedeniyle kısmen oldukça karmaşık bir manuel SQL kodu içerir, kısmen yazılı olarak (Rails 3.0), kısmen bilgi eksikliğinden dolayı makul bir alternatif nedeniyle (Umarım, en azından - bu çözülmesi kolay olurdu.Rails> 4.1 (with finder_sql)

Örnek: Bir InternalMessage sınıfı. Mesajlar kullanıcılar arasında (bir InternalMessage Alıcısı ve mesajların 'silinmesi') gönderilebilir, birkaç tane olabileceğinden, InternalMessagesRecipients'da saklanır ve okunabilir, iletilebilir, iletilebilir ve silinebilir. dernek şuna benzer:

class User < AR::Base 
    has_many :internal_messages, 
     :finder_sql => "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " + 
      ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' + 
      ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}', 
     :counter_sql => 'SELECT count(DISTINCT(internal_messages.id)) FROM internal_messages ' + 
      ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' + 
      ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}' 
    # ... 
end 

anahtar bölümü sonundaki "VEYA" deyimdir - ben hem alınan ve ayrı kullanıcı tablosuyla birleştirilmiş iletiler, gönderilen almak istiyorum bu dernek ile: bir InternalMessage beri

has_many :sent_messages, -> { where(:sender_deleted_at => nil) }, :class_name => 'InternalMessage', :foreign_key => 'sender_id' #, :include => :sender 
    has_many :internal_messages_recipients, :foreign_key => 'recipient_id' 
    has_many :rcvd_messages, :through => :internal_messages_recipients, :source => :internal_message, :class_name => 'InternalMessage' 

birkaç alıcıları olabilir (ve aynı zamanda gönderici kendisi gönderilebilir).

S: Bu finder_sql'u bir Rails 4.2 uyumlu has_many tanımına nasıl yüklerim?

cevap

4

Güncelleme

Bu hiç mantıklı bir süre önce öğrendim. Bir has_many ilişkisi en az bir yönde enjeksiyon bağlantılarına sahip olmalıdır, bu nedenle bir SQL yan tümcesinde "VEYA" anlamsızdır. Bir CREATE işlemi, yeni bir kayıt oluşturmak için hangi koşulun yerine getirileceğine nasıl karar vermelidir? Bu ilişki yalnızca tanım olarak okunur ve bu nedenle has_many ilişkisi değildir.

Bu durumda, basit bir sınıf yöntemi (veya kapsamı), has_many yerine doğru yanıt olabilir. (Yani @user.internal_messages.by_date vs.) elde edilen nesne chainable tutmak için çeşitli sorgulardan sonuçları

def internal_messages 
    InternalMessage.where(id: sent_message_ids + received_message_ids) 
end 

gibi bir şey kullanmak

+0

Elbette, sonuçların hafızaya alınmasını istiyorsanız özellikle "yeniden yükleme" ile çalışırlar. Yabancı ilişkileri olmayan bu ilişkileri sürdürmek için raylar almanın bir yolu var mı? – duane

3

SQL dizesini içeren probu kapsam olarak geçirin.

has_many :internal_messages, -> { proc { "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " + 
     ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' + 
     ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}' } } 
+0

Tamam, teşekkürler birleştirin. Counter_sql hakkında ne dersiniz? Artık gerekli değil? Rails 3 ile DISTINCT nedeniyle counter_sql kullanılmadığı zaman SQL hataları alıyorum. – Jens

+1

Bunu hiç kendim yapmadım ama [bu stackoverflow cevabı] (http://stackoverflow.com/questions/22988321/replacement-for-has-many-counter-sql-in-rails-4-1) diyor ki has_many'ye verilen bir blok içinde kendi sayım yönteminizi tanımlayabilirsiniz. Yani ben proxy_association.owner.class.count_by_sql ("SİZİN BURADA SİZİN") saymak gibi bir şey yapabilirsiniz sanırım; blokta biter. – tyamagu2

+4

güncelleme: Raylar 4.2.5.1, ruby ​​2.3.1p112 'has_many: görüşmeler, -> {proc {" SOME SQL "}}' NoMethodError: undefined method 'except' için # Dimitri