Respostas:
Digamos que você tenha dois modelos: User
e Group
.
Se você quiser que os usuários pertençam a grupos, faça algo assim:
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end
E se você quiser rastrear metadados adicionais em torno da associação? Por exemplo, quando o usuário ingressou no grupo, ou talvez qual a função do usuário no grupo?
É aqui que você faz da associação um objeto de primeira classe:
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :group
# has attributes for date_joined and role
end
Isso introduz uma nova tabela e elimina a group_id
coluna da tabela do usuário.
O problema com esse código é que você precisaria atualizar em todos os lugares em que usar a classe de usuário e alterá-la:
user.groups.first.name
# becomes
user.group_memberships.first.group.name
Esse tipo de código é péssimo e dificulta a introdução de alterações como essa.
has_many :through
oferece o melhor dos dois mundos:
class User < ActiveRecord::Base
has_many :groups, :through => :group_memberships # Edit :needs to be plural same as the has_many relationship
has_many :group_memberships
end
Agora você pode tratá-lo como um normal has_many
, mas obtenha o benefício do modelo de associação quando precisar.
Observe que você também pode fazer isso com has_one
.
Editar: facilitando adicionar um usuário a um grupo
def add_group(group, role = "member")
self.group_associations.build(:group => group, :role => role)
end
user.groups << group
? Ou tudo é tratado por essa associação?
Digamos que você tenha estes modelos:
Car
Engine
Piston
Um carro has_one :engine
Um motor belongs_to :car
Um motor has_many :pistons
Pistãobelongs_to :engine
Um carro de has_many :pistons, through: :engine
pistãohas_one :car, through: :engine
Essencialmente, você está delegando um relacionamento de modelo para outro modelo; portanto, em vez de precisar chamar car.engine.pistons
, bastacar.pistons
has_many :through
e os has_and_belongs_to_many
relacionamentos funcionam por meio de uma tabela de junção , que é uma tabela intermediária que representa o relacionamento entre outras tabelas. Ao contrário de uma consulta JOIN, os dados são realmente armazenados em uma tabela.
Com has_and_belongs_to_many
, você não precisa de uma chave primária e acessa os registros por meio das relações do ActiveRecord, e não por um modelo do ActiveRecord. Você costuma usar o HABTM quando deseja vincular dois modelos a um relacionamento muitos-para-muitos.
Você usa um has_many :through
relacionamento quando deseja interagir com a tabela de junção como um modelo Rails, completo com chaves primárias e a capacidade de adicionar colunas personalizadas aos dados ingressados. O último é particularmente importante para dados que são relevantes para as linhas unidas, mas realmente não pertencem aos modelos relacionados - por exemplo, armazenar um valor calculado derivado dos campos na linha unida.
Em Um guia para associações de registros ativos , a recomendação diz:
A regra mais simples é que você configure um relacionamento has_many: through se precisar trabalhar com o modelo de relacionamento como uma entidade independente. Se você não precisar fazer nada com o modelo de relacionamento, pode ser mais simples configurar um relacionamento has_and_belongs_to_many (embora seja necessário lembrar de criar a tabela de junção no banco de dados).
Você deve usar has_many: through se precisar de validações, retornos de chamada ou atributos extras no modelo de junção.
user
modelo para adicionar o grupo. Algo como a edição que acabei de fazer. Espero que isto ajude.