Возникли проблемы с обновлением дополнительных атрибутов модели соединения в has_many через ассоциацию - Rails

Я все еще изучаю Rails и делаю игру-симулятор Google Adwords для обучения. Внутри игры (я могу объяснить только связанную часть этого вопроса) у меня есть три модели: Adgroup (в моем коде называется KeywordAdgroup), Keyword, AdgroupKeyword, а также Модель игры, в которой есть game_id.

Взаимосвязь трех моделей представляет собой ассоциацию has_many through:, где Adgroup имеет много ключевых слов через AdgroupKeyword, а Keyword имеет много Adgroups через AdgroupKeyword.

Теперь я хочу принять логику, согласно которой каждое ключевое слово можно добавить только в одну группу объявлений конкретной игры. Поэтому я добавил в модель соединения дополнительный атрибут game_id — AdgroupKeyword, и внедрил в эту модель validates_uniqueness_of :keyword_id, scope: :game_id.

Однако я обнаружил, что с помощью аналогичного кода я могу проверить уникальность в Create с помощью метода .save, но не могу проверить уникальность в Update с помощью метода .update(params).

Основная проблема заключается в том, что с похожим кодом в create и update можно создать save game_id в модели присоединения (AdgroupKeyword), где update(params) не сохранил game_id в модели присоединения, что в консоли я могу посмотрите, что update не вставил в запись game_id.

Это станет чем-то вроде game_id: nil:

#<AdgroupKeyword id: 180, keyword_id: 9, created_at: "2016-12-04 11:12:00", updated_at: "2016-12-04 11:12:00", keyword_adgroup_id: 77, game_id: nil>

Но метод сохранения вставил game_id, который не равен нулю при вставке с использованием метода сохранения. Что-то типа

#<AdgroupKeyword id: 174, keyword_id: 10, created_at: "2016-12-04 10:23:50", updated_at: "2016-12-04 10:23:50", keyword_adgroup_id: 77, game_id: 3>

Ниже приведен мой код:

keyword_adgroup.rb (модель группы объявлений)

class KeywordAdgroup < ApplicationRecord
  belongs_to :keyword_campaign

  has_many :adgroup_keywords, inverse_of: :keyword_adgroup, dependent: :destroy
  has_many :keywords, through: :adgroup_keywords, source: :keyword
  accepts_nested_attributes_for :adgroup_keywords, allow_destroy: true

  ...

  accepts_nested_attributes_for :keywords

  ...

  ...
end

keyword.rb

class Keyword < ApplicationRecord
  ...

  ...

  has_many :keyword_adgroups, through: :adgroup_keywords
  has_many :adgroup_keywords

  ...
end

adgroup_keyword.rb (модель присоединения)

class AdgroupKeyword < ApplicationRecord
  belongs_to :keyword_adgroup
  belongs_to :keyword

  validates_uniqueness_of :keyword_id, scope: :game_id

end

И в основном связь будет построена в контроллере Adgroup.

adgroups_controller.rb

class Adwords::AdgroupsController < AdwordsController

  def index
    ...
  end

  def new
    @adgroup = KeywordAdgroup.new
  end

  def create
    @campaign = current_user.game.keyword_campaigns.find(params[:keyword_campaign_id])
    @adgroup = @campaign.keyword_adgroups.build(adgroup_params)
    @adgroup.game_id = @campaign.game_id
    @adgroup.adgroup_keywords.each do |join|
      join.game_id = @adgroup.game_id    
    end

    if @adgroup.save
      redirect_to new_adwords_ad_path(keyword_adgroup_id: @adgroup.id)
    else
      render :new
    end
  end

  def edit
    @adgroup = KeywordAdgroup.find(params[:id])
  end

  def update
    @adgroup = KeywordAdgroup.find(params[:id])
    @campaign = @adgroup.keyword_campaign
    @adgroup.game_id = @campaign.game_id
    @joins = @adgroup.adgroup_keywords
    @joins.each do |join|
      join.game_id = @adgroup.game_id  
    end 
    if @adgroup.update(adgroup_params)

      redirect_to adwords_adgroups_path, notice: "Adgroup updated"
    else
      render :edit
    end
  end

  ...

  private
  def adgroup_params
    params.require(:keyword_adgroup).permit(:name, :activate, :bid, keyword_ids: [], adgroup_keywords_attributes: [:game_id])
  end

end

При выборе повторяющегося ключевого слова в одной и той же игре в любой группе объявлений, в "создать" произойдет откат; в «обновлении» он просто обновит запись на game_id: nil.

К вашему сведению, я использую Ruby 2.3.0, Rails 5.0.0 и использую простую форму для создания и обновления группы объявлений.

Я знаю, что это немного грязно, извините за это и спасибо, что прочитали мои слова. Цените любые предложения. Уже потратил несколько часов, чтобы исправить это, но действительно безнадежно после множества разных методов. Думал, что это какая-то концептуальная проблема, которую я не понял.


Обновленная информация

При проверке консоли в методе «Создать» будет:

  AdgroupKeyword Exists (0.4ms)  SELECT  1 AS one FROM "adgroup_keywords" WHERE "adgroup_keywords"."keyword_id" = ? AND "adgroup_keywords"."game_id" = ? LIMIT ?  [["keyword_id", 14], ["game_id", 3], ["LIMIT", 1]]
  Keyword Exists (0.3ms)  SELECT  1 AS one FROM "keywords" WHERE "keywords"."keyword" = ? AND ("keywords"."id" != ?) LIMIT ?  [["keyword", "Japan local trip"], ["id", 14], ["LIMIT", 1]]
  KeywordAdgroup Exists (0.2ms)  SELECT  1 AS one FROM "keyword_adgroups" WHERE "keyword_adgroups"."name" = ? AND "keyword_adgroups"."keyword_campaign_id" = ? LIMIT ?  [["name", "3"], ["keyword_campaign_id", 36], ["LIMIT", 1]]
  SQL (2.3ms)  INSERT INTO "keyword_adgroups" ("name", "activate", "bid", "created_at", "updated_at", "keyword_campaign_id", "game_id") VALUES (?, ?, ?, ?, ?, ?, ?)  [["name", "3"], ["activate", true], ["bid", #<BigDecimal:7fcaac52c320,'0.1E1',9(18)>], ["created_at", 2016-12-04 13:10:19 UTC], ["updated_at", 2016-12-04 13:10:19 UTC], ["keyword_campaign_id", 36], ["game_id", 3]]
  SQL (0.2ms)  INSERT INTO "adgroup_keywords" ("keyword_id", "created_at", "updated_at", "keyword_adgroup_id", "game_id") VALUES (?, ?, ?, ?, ?)  [["keyword_id", 14], ["created_at", 2016-12-04 13:10:19 UTC], ["updated_at", 2016-12-04 13:10:19 UTC], ["keyword_adgroup_id", 84], ["game_id", 3]]
  AdgroupKeyword Exists (0.3ms)  SELECT  1 AS one FROM "adgroup_keywords" WHERE "adgroup_keywords"."keyword_id" = ? AND ("adgroup_keywords"."id" != ?) AND "adgroup_keywords"."game_id" = ? LIMIT ?  [["keyword_id", 14], ["id", 187], ["game_id", 3], ["LIMIT", 1]]

В методе «Обновить» это будет:

  AdgroupKeyword Exists (0.2ms)  SELECT  1 AS one FROM "adgroup_keywords" WHERE "adgroup_keywords"."keyword_id" = ? AND "adgroup_keywords"."game_id" IS NULL LIMIT ?  [["keyword_id", 17], ["LIMIT", 1]]
  SQL (2.0ms)  INSERT INTO "adgroup_keywords" ("keyword_id", "created_at", "updated_at", "keyword_adgroup_id") VALUES (?, ?, ?, ?)  [["keyword_id", 17], ["created_at", 2016-12-04 13:07:03 UTC], ["updated_at", 2016-12-04 13:07:03 UTC], ["keyword_adgroup_id", 82]]
  AdgroupKeyword Exists (0.2ms)  SELECT  1 AS one FROM "adgroup_keywords" WHERE "adgroup_keywords"."keyword_id" = ? AND ("adgroup_keywords"."id" != ?) AND "adgroup_keywords"."game_id" = ? LIMIT ?  [["keyword_id", 11], ["id", 185], ["game_id", 3], ["LIMIT", 1]]

person Joe Leung    schedule 04.12.2016    source источник


Ответы (1)


Можете ли вы попробовать запустить этот блок после вызова #update (внутри блока if)?

@adgroup.reload

@adgroup.adgroup_keywords.each do |join|
  join.update(:game_id, @adgroup.game_id)
end
person André Guimarães Sakata    schedule 04.12.2016
comment
О, вы правы, потому что в моем создании нужно было сделать первый шаг по выбору кампании с ключевыми словами, поэтому передайте id_кампании и создайте группу объявлений. Но почему и как это повлияет на мой метод обновления? Большое спасибо! - person Joe Leung; 04.12.2016
comment
Я только что обновил свой ответ - можете ли вы проверить, действительно ли он передает значение game_id? - person André Guimarães Sakata; 04.12.2016
comment
Спасибо, я обновил информацию внизу. Я пытался удалить его из разрешенных параметров раньше, но безуспешно. Чувствовать себя сбитым с толку часами. - person Joe Leung; 04.12.2016
comment
Спасибо. Можете ли вы напечатать adgroup_params непосредственно перед вызовом #update? - person André Guimarães Sakata; 04.12.2016
comment
Я подумал о чем-то вроде: я установил game_id для каждого ключевого слова adgroup_keyword (модель присоединения) группы объявлений, но @adgroup.save знает, что нужно сохранить game_id в базе данных; @adgroup.update(adgroup_params) не знает, как сохранить game_id в базе данных. Но как мне это исправить? - person Joe Leung; 04.12.2016
comment
Упс, извините за опоздание. Я просто использую puts adgroup_params.inspect и получаю: ‹ActionController::Parameters {name=›2, bid=›1.0, keyword_ids=›[, 13, 17]} разрешено: true› - person Joe Leung; 04.12.2016
comment
Хорошо, теперь я действительно понимаю проблему ... только что обновил свой ответ, можете попробовать еще раз? - person André Guimarães Sakata; 04.12.2016
comment
Пробовали. Он по-прежнему вставляется без game_id, но после вставки модели соединения это вызовет некоторый откат из-за join.update(game_id: @adgroup.gam_id). Я пытался сделать это перед циклом, но все равно вставляю без game_id. Я действительно не могу понять, почему. - person Joe Leung; 04.12.2016
comment
Можете ли вы попробовать сделать @adgroup.reload перед циклом? Это должно быть после update. - person André Guimarães Sakata; 04.12.2016
comment
Тем не менее, программа вставляет все записи без game_id во время @adgroup.update(adadgroup_params) до того, как будет выполнена перезагрузка, и выполняет откат во время join.update. Как я могу обновить ключевое слово adgroup_keyword (модель соединения) с одновременным обновлением game_id? Нет ли способа обновить, но создать дополнительный атрибут модели соединения? - person Joe Leung; 04.12.2016