Работа через ассоциацию с несколькими идентификаторами (Rails 4.2.3)

#ruby-on-rails #ruby #ruby-on-rails-4

#ruby-on-rails #ruby #ruby-on-rails-4

Вопрос:

Я работаю над своим первым проектом Rails. Я создаю приложение, в котором пользователь входит в систему и создает плату, и эта плата зависит от идентификатора пользователя. Внутри этой платы пользователь может создавать списки. Каждый список должен быть привязан к идентификатору этой платы. Проблема, с которой я сталкиваюсь, связана с созданием списка. Это не сохраняется. Конкретная ошибка, которую я вижу, заключается Could not find the association :memberships in model List в том, что я вижу, что существует проблема с сопоставлением списка с идентификатором платы. Когда я просматриваю выходные данные с сервера Rails, я вижу, что отображение платы (которая должна отображать все списки для этой платы) не имеет идентификатора пользователя или идентификатора платы.

Отображение плат для пользователя

 Started GET "/boards" for ::1 at 2016-10-12 14:26:33 -0400
Processing by BoardsController#index as HTML
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  Board Load (0.1ms)  SELECT "boards".* FROM "boards" WHERE "boards"."user_id" = ?  [["user_id", 1]]
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  Rendered boards/index.html.erb within layouts/application (3.4ms)
  Rendered layouts/_flash.html.erb (0.1ms)
Completed 200 OK in 168ms (Views: 166.1ms | ActiveRecord: 0.2ms)
  

ПРОТИВ отображения списков для этого пользователя

 Started GET "/lists.3" for ::1 at 2016-10-12 14:28:26 -0400
Processing by ListsController#index as
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  List Load (0.3ms)  SELECT "lists".* FROM "lists"
  Rendered lists/index.html.erb within layouts/application (2.3ms)
  Rendered layouts/_flash.html.erb (0.1ms)
Completed 200 OK in 181ms (Views: 169.2ms | ActiveRecord: 0.9ms)
  

Соответствующий код …

Модели

board.rb

     class Board < ActiveRecord::Base
  belongs_to :owner, class_name: "User", foreign_key: "user_id"

  has_many :lists
  has_many :memberships
  has_many :members, :through => :memberships, :source => :user
  has_many :lists, :through => :memberships, :source => :board

  validates_presence_of :title
  validates_presence_of :owner

  def is_private?
    return self.is_private
  end
end
  

list.rb

 class List < ActiveRecord::Base
  belongs_to :board
  has_many :cards

  has_one :board, :through => :memberships, :source => :board

  validates_presence_of :title
  validates_presence_of :board
end
  

user.rb

 class User < ActiveRecord::Base
  has_many :boards
  has_many :lists
  has_many :cards
  has_many :memberships
  has_many :board_memberships, :through => :memberships, :source => :board
  has_many :list_memberships, :through => :memberships, :source => :board

  has_secure_password

  validates :password,
    length: { :minimum => 8 },
    presence: true

  validates :username,
    uniqueness: true,
    presence: true

  validates_confirmation_of :password
end
  

membership.rb

 class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :board

  validates :user, presence: true, uniqueness: { scope: :board, message: "User is already a member of that board" }
  validates :board, presence: true
end
  

Controllers

boards_controller.rb (I’ve only added the relevant parts here)

     class BoardsController < ApplicationController
      before_action :set_board, only: [:show, :edit, :update, :destroy]
      before_action :authenticate, except: [:show]
      before_action :authorize, except: [:show]
      before_action :authorize_owner, only: [:edit, :update, :destroy]
      before_action :authorize_membership, only: [:show]

      # GET /boards
      # GET /boards.json
      def index
        @boards = current_user.boards
      end

      # GET /boards/1
      # GET /boards/1.json
      def show
        @lists = current_user.board_id
      end

    private
        def set_board
          @board = Board.find(params[:id])
        end

        def board_params
          params.require(:board).permit(:title, :is_private)
        end

        def authorize_owner
          if current_user != @board.owner
            redirect_to root_url, :notice => 'Board not found or inaccessible' and return
          end
        end

        def authorize_membership
          return
          if @board.is_private?
            if !authenticate
              redirect_to root_url, :notice => 'Please login first' and return
            end

            if @board.owner != current_user || !@board.members.include?(current_user)
              redirect_to root_url, :notice => 'Board not found or inaccessible' and return
            end
          end
        end
  # POST /boards
  # POST /boards.json
  def create
    @board = Board.new(board_params)
    @board.owner = current_user

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

lists_controller.rb (весь файл)

 class ListsController < ApplicationController
  before_action :set_list, only: [:show, :edit, :update, :destroy]
  before_action :authenticate, except: [:show]

  # GET /lists
  # GET /lists.json
  def index
    @lists = List.all
  end

  # GET /lists/1
  # GET /lists/1.json
  def show
    @lists = current_user.board_id
  end

  # GET /lists/new
  def new
    @list = List.new
  end

  # GET /lists/1/edit
  def edit
  end

  # POST /lists
  # POST /lists.json
  def create
    @list = List.new(list_params)

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

  # PATCH/PUT /lists/1
  # PATCH/PUT /lists/1.json
  def update
    respond_to do |format|
      if @list.update(list_params)
        format.html { redirect_to @list, notice: 'List was successfully updated.' }
        format.json { render :show, status: :ok, location: @list }
      else
        format.html { render :edit }
        format.json { render json: @list.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /lists/1
  # DELETE /lists/1.json
  def destroy
    @list.destroy
    respond_to do |format|
      format.html { redirect_to lists_url, notice: 'List was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_list
      @list = List.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def list_params
      params.require(:list).permit(:title, :board_id)
    end
end
  

Я прочитал несколько сообщений SO о вложенных моделях, а также документацию Rails. Я знаю, что у меня где-то есть пробел в знаниях. Я был бы признателен за любое направление, поскольку мои попытки прочитать чужие вопросы и попробовать решения не увенчались успехом.

Комментарии:

1. Получаете ли вы какие-либо сообщения о том, что некоторые параметры не разрешены?

2. Вы начали отладку с помощью каких-либо инструкций журнала для вывода идентификатора пользователя или платы в контроллере?

3. кстати, вам нужны членства в has_many: при использовании through?

4. Правильно ли это? has_many : списки, : через => : членства, :source => :board как включенные board , так и включенные user

5. Я даже не вижу create для плат вообще

Ответ №1:

Если вы хотите List , чтобы объект мог получить доступ :memberships к Board объекту, тогда сделайте…

 class List << ActiveRecord::Base

  delegate :memberships, to: :board
  

Удалите
has_one :board, :through => :memberships, :source => :board , поскольку это не имеет смысла… у вас уже есть отношения с доской, и у вас нет отношений членства, которые можно было бы использовать для создания других отношений с доской.