chell
chell

Reputation: 117

Working through an Association with multiple ID's (Rails 4.2.3)

I am working through my first Rails project. I am creating an application where a user logs in and creates a board, and this board is specific to the user's ID. Inside that board, the user can create lists. Each list should be tied the ID of that board. The issue I'm having is with creating a list. It does not save. The specific error I'm seeing is Could not find the association :memberships in model List I can see there is an issue with the List being mapped to the Board's ID. When I'm looking at the output from the Rails server, I see that showing a board (which should show all the lists for that board) does not have a user ID or a board ID.

Showing the boards for a user

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)

VS Showing the lists for that user

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)

Relevant code ...

Models

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 || [email protected]?(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 (entire file)

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

I have read through the several SO posts about nested models, as well as the Rails documentation. I know I have a knowledge gap somewhere. I would appreciate any direction, as my attempts at reading others' questions and trying solutions have failed.

Upvotes: 1

Views: 304

Answers (1)

SteveTurczyn
SteveTurczyn

Reputation: 36860

If you want the List object to be able to access the :memberships of the Board object then do...

class List << ActiveRecord::Base

  delegate :memberships, to: :board

Remove the has_one :board, :through => :memberships, :source => :board since that doesn't make sense... you already have a board relationship and you have no membership relationships to use to make a different board relationship.

Upvotes: 1

Related Questions