Reputation: 117
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
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