ActiveModel :: Ошибка отсутствующего атрибута в Accounts :: RetreatsController # create - PullRequest
0 голосов
/ 10 февраля 2020

Я просмотрел StackOverflow и не смог найти решение для моего варианта использования здесь.

Я пытаюсь прикрепить команду или несколько команд к отступлениям при создании отступлений.

У меня мультитенантное приложение, у которого есть владелец аккаунта, который приглашает пользователей. Пользователи могут быть добавлены в команды.

Я могу успешно создать ретрит, если ни одна команда не выбрана в форме, однако, когда команда выбрана и ретрит создан с этой выбранной командой, я получаю следующую ошибку:

ActiveModel::MissingAttributeError in Accounts::RetreatsController#create

can't write unknown attribute `retreat_id`

Parameters:

    {"authenticity_token"=>"AcmWMmv+rhwzLKsxi9RykkhhquaZuTBqSBj87acOdDOo+uXycQOWHoeuNzDEGTQw+o7ewwS4EB8uTyIy7x5vsA==",
     "retreat"=>{"name"=>"bbb", "description"=>"dsfsdf", "team_ids"=>["56"]},
     "commit"=>"Create Retreat"}

Вот мои модели:

team.rb

class Team < ApplicationRecord
  belongs_to :account
  has_many_and_belongs_to :retreat

  has_many :team_members
  has_many :users, through: :team_members
  accepts_nested_attributes_for :team_members
end

retreat.rb

class Retreat < ApplicationRecord
  belongs_to :user
  belongs_to :account

  validates :name, presence: true, uniqueness: true

  has_many :teams
  accepts_nested_attributes_for :teams
end

Это контроллер:

module Accounts
  class RetreatsController < Accounts::BaseController
    before_action :authenticate_user!
    before_action :set_retreat, only: [:show, :edit, :update, :destroy]

    def index
      @retreats = current_account.retreats.all.order("created_at DESC")
    end

    def show
    end

    def new
      @retreat = current_account.retreats.build
      @team = current_account.teams.all
    end

    def edit
    end

    def create
      @retreat = current_account.retreats.build(retreat_params)
      @retreat.user_id = current_user.id

      respond_to do |format|
        if @retreat.save
          format.html { redirect_to @retreat, notice: 'Retreat was successfully created.' }
          format.json { render :show, status: :created, location: @retreat }
        else
          format.html { render :new }
          format.json { render json: @retreat.errors, status: :unprocessable_entity }
        end
      end
    end

    def destroy
      @retreat.destroy
      respond_to do |format|
        format.html { redirect_to root_path, notice: 'Retreat was successfully deleted.' }
        format.json { head :no_content }
      end
    end

    private

      def set_retreat
        @retreat = current_account.retreats.find(params[:id])
      end

      def retreat_params
        params.require(:retreat).permit(:name, :description, team_ids:[])
      end
  end
end

Это представление:

<div class="w-full lg:w-12/12 bg-white p-5 rounded-lg lg:rounded-l-none">
  <%= simple_form_for(@retreat) do |f| %>
    <%= f.error_notification %>
    <form class="px-8 pt-6 pb-8 mb-4 bg-white rounded">
      <div class="mb-4">
        <div class="w-full mb-4 md:mr-2 md:mb-0">
          <label class="block mb-2 text-sm font-bold text-gray-700" for="firstName">
            Name your retreat
          </label>
        <%= f.input :name, autofocus: true, class: 'w-full px-3 py-2 mb-3 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline', label: false %>
      </div>
     </div>
     <div class="mb-4">
      <div class="w-full mb-4 md:mr-2 md:mb-0">
        <label class="block mb-2 text-sm font-bold text-gray-700" for="firstName">
          Give it a description
        </label>
        <%= f.text_area :description, autofocus: true, class: 'no-resize appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500 h-48 resize-none', label: false %>
      </div>
     </div>

     <div class="field">
      <div class="control">
        <%= f.label "Add team(s) to your retreat", class:"label" %><br />
        <div class="control has-icons-left">
          <%= f.collection_check_boxes :team_ids, current_account.teams.all, :id, :name, include_hidden: false do |b| %>
            <div class="collection-check-box">
              <%= b.check_box %>
              <%= b.label %>
            </div>
          <% end %>
        </div>
      </div>
     </div>

     <div class="field">
      <div class="control">
        <%= f.button :submit, class: "w-full px-4 py-2 font-bold text-white rounded-full bg-green-500 hover:bg-green-700 focus:outline-none focus:shadow-outline" %>
      </div>
     </div>
     <%= link_to 'Back', retreats_path, class:'button' %>
    </form>
  <% end %>
</div>

Вот моя схема:

create_table "retreats", force: :cascade do |t|
    t.string "name"
    t.text "description"
    t.bigint "user_id", null: false
    t.bigint "account_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "team_id"
    t.index ["account_id"], name: "index_retreats_on_account_id"
    t.index ["user_id"], name: "index_retreats_on_user_id"
  end

create_table "teams", force: :cascade do |t|
    t.string "name"
    t.bigint "account_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["account_id"], name: "index_teams_on_account_id"
  end

  create_table "team_members", force: :cascade do |t|
    t.bigint "team_id", null: false
    t.bigint "user_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["team_id"], name: "index_team_members_on_team_id"
    t.index ["user_id"], name: "index_team_members_on_user_id"
  end

Ответ

В итоге я добавил таблицу соединения между и команды по отступлению, и команды по отступлению, и объединяющий стол со ссылками как на отступление, так и на команды.

В принятом комментарии предлагалось использовать ассоциацию has_and_belongs_to_many, но я считаю, что это не рекомендуется для текущих версий rails. И, следовательно, используется сквозной, который принимает вложенные атрибуты.

1 Ответ

0 голосов
/ 10 февраля 2020

Требуется связь между многими из Retreat и Team. Для этого вам необходимо создать соединительную таблицу retreats_teams со столбцами retreat_id и team_id, а затем

В Retreat модель:

has_and_belongs_to_many :teams

В Team модель :

has_and_belongs_to_many :retreats

Более подробную информацию о связях «многие ко многим» можно найти в « Выбор между has_many :through и has_and_belongs_to_many».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...