has_many: через has_and_belongs_to_many в Rails - PullRequest
1 голос
/ 23 февраля 2012

В Rails - каков эффект от использования has_many: через has_and_belongs_to_many? Рассмотрите возможность использования двух моделей - сообщений и тегов, которые имеют отношение «многие ко многим», как показано ниже:

class Tag < ActiveRecord::Base
  has_many :posts_tag
  has_and_belongs_to_many :posts
end

class Post < ActiveRecord::Base
  has_many :posts_tag
  has_many :tags,  :through => posts_tag
end

class PostsTag < ActiveRecord::Base
  belongs_to :tag
  belongs_to :post
end

Причина, по которой я использую has_and_belongs_to_many, заключается в том, что tag принадлежит многим постам.

Я заглянул в руководство Rails Association и увидел, что они не упоминают этот случай для отношений "многие ко многим". Я, однако, попробовал это, и запуск его в Rails не дал никакого поведения, и из небольшой тестовой базы данных, которую я построил, также, казалось, возвращал правильные результаты для post.tags и tag.posts - где post и tag относится к экземпляру моделей Post и Tag соответственно.

Это правильное использование или оно имеет какие-либо побочные эффекты, о которых я не знаю? Кроме того, если это правильно, это Rails способ достижения этого?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 23 февраля 2012

Вы используете has_and_belongs_to_many только тогда, когда вы устанавливаете связь «многие ко многим» (другими словами, когда на другой стороне также есть has_and_belongs_to_many). В этом смысл этой ассоциации.

Вы должны иметь

class Tag < ActiveRecord::Base
  has_many :posts_tags
  has_many :posts, :through => :post_tags
end

class PostsTag < ActiveRecord::Base
  belongs_to :tag
  belongs_to :post
end

class Post < ActiveRecord::Base
  has_many :posts_tags
  has_many :tags, :through => :posts_tags
end

Обратите внимание, что я использовал множественное число, post_tags (потому что это правильный путь).

Если у вас есть ситуация, как в вашем комментарии, вы должны иметь

belongs_to :post_tag

в вашей Post модели и

has_many :posts

в вашей PostTag модели.

Теперь вы можете спросить: «Почему я должен использовать belongs_to :post_tag? Он не принадлежит тегу, он имеет тега. Поэтому я не должен использовать has_one :post_tag?». Сначала это был и мой вопрос, но потом я понял, что Rails не всегда может идеально соответствовать английскому языку. Вам нужен столбец post_tag_id на вашем post, и belongs_to ожидает именно этого. С другой стороны, has_one будет ожидать, что столбец с именем post_id присутствует на другой стороне , то есть на вашем post_tag. Но это было бы невозможно, потому что post_tag имеет много posts (не только один), поэтому идентификаторы post нельзя хранить в post_tags.

Обновление
Разница между ассоциациями заключается только в предоставляемых вами методах и опциях, которые вы можете передать (объяснено в руководстве Rails по ассоциациям). Например, has_one и belongs_to имеют одинаковые методы:

association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})

Но, например, методы association= и create_association подразумевают разные вещи относительно того, где должен находиться внешний ключ (как я объяснил выше).

has_and_belongs_to_many и has_many, вероятно, не имеют ничего отличного в своих методах, но они отличаются в вариантах, которые вы можете передать. Например, вы можете передать в

:dependent => :destroy

в ассоциации has_many, но вы не можете передать ее в has_and_belongs_to_many, потому что это не имеет смысла, поскольку подразумевает связь многих со многими; если родительская запись уничтожена, дочерние записи все равно могут быть связаны с другими записями, поэтому их также не следует уничтожать.

1 голос
/ 23 февраля 2012

Хотя я не уверен в точных последствиях наличия has_many :through на одной стороне отношения и has_and_belongs_to_many на другой стороне, я знаю, что более правильным будет использование обратного has_many :through вроде так:

class Tag < ActiveRecord::Base
  has_many :posts_tag
  has_many :posts,  :through => posts_tag
end

Сохраняя остальные отношения такими же.

...