Ruby on Rails - Refereça o mesmo modelo duas vezes?
-
20-09-2019 - |
Pergunta
É possível configurar um duplo relacionamento em activerecord
modelos via generate scaffold
comando?
Por exemplo, se eu tivesse um User
modelo e a PrivateMessage
modelo, a tabela PM precisaria acompanhar os dois sender
e recipient
.
Obviamente, para um único relacionamento, eu faria isso:
ruby script/generate scaffold pm title:string content:string user:references
Existe uma maneira semelhante de estabelecer duas relações?
Além disso, existe de qualquer maneira para configurar aliases para as relações?
Então, em vez de dizer:
@message.user
Você pode usar algo como:
@message.sender
ou @message.recipient
Qualquer conselho seria muito apreciado.
Obrigado.
Solução
Adicione isso ao seu modelo
has_one :sender, :class_name => "User"
has_one :recipient, :class_name => "User"
E você é capaz de ligar @message.sender
e @message.recipient
e ambas as referências ao modelo de usuário.
Ao invés de user:references
No seu comando de geração, você precisaria sender:references
e recipient:references
Outras dicas
Aqui está uma resposta completa a esse problema, caso as pessoas que visitem essa pergunta sejam novas no Ruby on Rails e tenham dificuldade em reunir tudo (como eu estava quando eu olhei para isso pela primeira vez).
Algumas partes da solução ocorrem em suas migrações e outras em seus modelos:
Migrações
class CreatePrivateMessages < ActiveRecord::Migration
def change
create_table :private_messages do |t|
t.references :sender
t.references :recipient
end
# Rails 5+ only: add foreign keys
add_foreign_key :private_messages, :users, column: :sender_id, primary_key: :id
add_foreign_key :private_messages, :users, column: :recipient_id, primary_key: :id
end
end
Aqui você está especificando que existem duas colunas nesta tabela que serão chamadas de: remetente e: destinatário e que mantêm referências a outra tabela. Na verdade, os Rails criarão colunas chamadas 'sender_id' e 'receptor_id' para você. No nosso caso, cada uma delas referenciará linhas na tabela de usuários, mas especificamos que nos modelos, não nas migrações.
Modelos
class PrivateMessage < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
Aqui você está criando uma propriedade no modelo privatemessage chamado: remetente e, em seguida, especificando que esta propriedade está relacionada à classe de usuário. Os Rails, vendo o "pertencente_to: remetente", procurarão uma coluna no seu banco de dados chamado "sender_id", que definimos acima, e usará isso para armazenar a chave estrangeira. Então você está fazendo exatamente a mesma coisa para o destinatário.
Isso permitirá que você acesse seu remetente e destinatário, ambas as instâncias do modelo de usuário, por meio de uma instância do modelo privatemessage, como este:
@private_message.sender.name
@private_message.recipient.email
Aqui está o seu modelo de usuário:
class User < ActiveRecord::Base
has_many :sent_private_messages, :class_name => 'PrivateMessage', :foreign_key => 'sender_id'
has_many :received_private_messages, :class_name => 'PrivateMessage', :foreign_key => 'recipient_id'
end
Aqui você está criando uma propriedade no modelo de usuário chamado: Sent_private_messages, especificando que essa propriedade está relacionada ao modelo privatemessage e que a chave estrangeira no modelo privatemessage que o relaciona a esta propriedade é chamada de 'sender_id'. Então você está fazendo a mesma coisa por mensagens privadas recebidas.
Isso permite que você receba todos os usuários ou recebidos mensagens privadas fazendo algo assim:
@user.sent_private_messages
@user.received_private_messages
Fazer qualquer um deles retornará uma variedade de instâncias do modelo privatemessage.
....
Olá, para ter a relação de ambos os laterais, faça o que está abaixo nos dois modelos:
class Message < ActiveRecord::Base
belongs_to :sender,
:class_name => "User",
:foreign_key => "sender_id"
belongs_to :recipient,
:class_name => "User",
:foreign_key => "recipient_id"
end
class User < ActiveRecord::Base
has_many :sent,
:class_name => "Message",
:foreign_key => "sent_id"
has_many :received,
:class_name => "Message",
:foreign_key => "received_id"
end
Espero que isso lhe ajude...
As respostas acima, embora excelentes, não criam restrições de chave estrangeira no banco de dados, apenas criando apenas índices e colunas BIGINT. Para garantir que a restrição de chave estrangeira seja aplicada, adicione o seguinte à sua migração:
class CreatePrivateMessages < ActiveRecord::Migration[5.1]
def change
create_table :private_messages do |t|
t.references :sender
t.references :recipient
end
add_foreign_key :private_messages, :users, column: :sender_id, primary_key: :id
add_foreign_key :private_messages, :users, column: :recipient_id, primary_key: :id
end
end
Isso garantirá que os índices sejam criados no sender_id
e recipient_id
bem como as restrições de chave estrangeira no banco de dados que você está usando.