Model と画面上の form が1対1で一致しない場合、どのように実装するのが綺麗なのか?

単純な複数選択の場合には、こういうのも好きです。

# class Article
def category_names
  categories.map(&:name)
end
def category_names=(names)
  self.categories = names.select(&:present?).map { |name| Category.find_or_initialize_by(name: name) }
end

ビューはselect2みたいな感じで。category_names[] を複数送ればOK。