Rails: работа с таблицей соединений во вложенной форме

У меня есть две модели, связанные таблицей соединений:

class Publication < ActiveRecord::Base
  attr_accessible :title, :author_attributes, :translator_attributes

  has_many :publication_contributors
  has_many :authors, :through => :publication_contributors, :source => :contributor,
    :conditions => {:publication_contributors => {:contributor_type => "Author"}}
  has_many :translators, :through => :publication_contributors, :source => :contributor,
    :conditions => {:publication_contributors => {:contributor_type => "Translator"}}

  accepts_nested_attributes_for :authors, :translators
end

class Contributor < ActiveRecord::Base
  attr_accessible :name

  has_many :publications, :through => :publication_contributors
  has_many :publication_contributors
end

class PublicationContributor < ActiveRecord::Base
  attr_accessible :contributor_type

  belongs_to :publication
  belongs_to :contributor
end

Обратите внимание, что у таблицы соединения есть третий атрибут (помимо публикации_id и вкладчика_идентификатора), который называется вкладчик_тип. Этот атрибут может содержать такую ​​роль, как «Автор», «Переводчик», «Редактор» или что-то еще. В моей модели публикации я создал пару ассоциаций для двух наиболее распространенных типов участников: «Автор» и «Переводчик». Эти ассоциации хорошо работают при извлечении соответствующих данных, например, с помощью @publication.authors. Однако при создании этих ассоциаций через вложенную форму он тормозит.

Моя форма выглядит примерно так:

<%= form_for @publication, :remote => true do |f| %>

  <%= f.label :title %>:
  <%= f.text_field :title %>

  <%= f.fields_for :authors do |builder| %>
    <%= builder.label :name, "Author" %>:
    <%= builder.text_field :name %>
  <% end %>

  <%= f.fields_for :translators do |builder| %>
    <%= builder.label :name, "Translator" %>:
    <%= builder.text_field :name %>
  <% end %>

  <%= f.submit %>
<% end %>

В моем контроллере:

def create
  publication = Publication.create(params[:publication])
end

Форма отображается, как и ожидалось, но во время действия создания она зависает. Я надеялся, что Rails узнает, как волшебным образом назначить правильный contributor_type на основе условий в ассоциациях Publication, таких как:

has_many :authors, :through => :publication_contributors, :source => :contributor,
:conditions => {:publication_contributors => {:contributor_type => "Author"}}

К сожалению, это не так. Я получаю эту ошибку во время создания:

Mysql2::Error: Column 'contributor_type' cannot be null

Это заставляет меня думать, что мой единственный выход — вручную назначить contributor_type в обратном вызове before_create, но если это так, как мне определить, какой тип назначить? Параметры имеют правильный тип в своем имени, например:

publication[authors_attributes][0][name]

Есть ли способ получить доступ к этой информации на уровне модели?

ОБНОВИТЬ:

Мое new действие:

def new
  @publication = Publication.new
  publication_contributor = @publication.publication_contributors.build
  contributor = publication_contributor.contributor.build
end

Он выдает эту ошибку в последней строке (настройка contributor):

undefined method `build' for nil:NilClass

person nullnullnull    schedule 05.02.2013    source источник
comment
Можете ли вы попробовать вместо этого добавить скрытое поле в форму?   -  person jvnill    schedule 05.02.2013
comment
Я не уверен, что это поможет. Атрибут contributor_type находится в таблице соединений, а не в модели участников. Поскольку я на самом деле не объявляю таблицу соединения в форме, у меня нет доступа к ее атрибутам, даже со скрытым полем. Однако, возможно, если есть способ создать таблицу соединений в форме, этот подход может сработать. Я посмотрю на это. Дайте мне знать, если у вас есть другие предложения.   -  person nullnullnull    schedule 05.02.2013
comment
ты прав. это нормально, если вы пройдете через publish_contributor, а затем через contributor?   -  person jvnill    schedule 05.02.2013
comment
Я сделаю этот выстрел и посмотрю, как это работает.   -  person nullnullnull    schedule 05.02.2013
comment
Не уверен, что это возможно. Чтобы заставить его работать, мне пришлось бы создать экземпляр Contributor вне PublicationContributor следующим образом: publication_contributor.contributor.new. Это вызывает ошибку undefined method 'new' for nil:NilClass. Я получаю те же результаты с .build. Интересно, не поддерживает ли ActiveRecord вложение от дочернего к родительскому? В документах эта ситуация не упоминается. так или другой.   -  person nullnullnull    schedule 05.02.2013
comment
это должно быть возможно. но поскольку вы используете accepts_nested_attributes_for, вам нужно создавать объекты. Я опубликую ответ для этого решения   -  person jvnill    schedule 05.02.2013


Ответы (1)


Можете ли вы попробовать следующее?

class Publication < ActiveRecord::Base
  attr_accessible :title, :publication_contributors_attributes

  has_many :publication_contributors
  has_many :authors, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Author' } }
  has_many :translators, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Translator' } }

  accepts_nested_attributes_for :publication_contributors
end

class Contributor < ActiveRecord::Base
  attr_accessible :name

  has_many :publications, through: :publication_contributors
  has_many :publication_contributors
end

class PublicationContributor < ActiveRecord::Base
  attr_accessible :contributor_type

  belongs_to :publication
  belongs_to :contributor

  accepts_nested_attributes_for :contributor
end

<%= form_for @publication, :remote => true do |f| %>

  <%= f.label :title %>:
  <%= f.text_field :title %>

  <%= f.fields_for :publication_contributors do |pc_form| %>
    <%= pc_form.hidden_field :contributor_type %>
    <%= pc_form.fields_for :contributor do |c_form| %>
      <%= c_form.label :name, "Author" %>:
      <%= c_form.text_field :name %>
    <% end %>
  <% end %>

  <%= f.submit %>
<% end %>

ОБНОВЛЕНИЕ: код контроллера

@publication.publication_contributors.build
@publication.publication_contributors.each do |pc|
  pc.build_contributor
end
person jvnill    schedule 05.02.2013
comment
Сделал этот снимок, но я все еще получаю ошибку сборки. Любопытно, что аналогичная ситуация работает и с этим RailsCast. - person nullnullnull; 05.02.2013
comment
я обновил свой ответ, указав, как должен выглядеть ваш контроллер :) - person jvnill; 05.02.2013
comment
Конечно! Несмотря на то, что я создал только одну из них, публикация_авторов все еще была ассоциацией. Любопытно то, что я не могу установить его с помощью publication_contributors = @publication.publication_contributors.build, а затем напрямую получить к нему доступ с помощью publication_contributors.each. Однако ваш код работает отлично. Так что спасибо! Меня просто сбивает с толку тот факт, что я не могу напрямую получить доступ к publish_contributors. - person nullnullnull; 05.02.2013