Reputation: 309
I'm having an error which most likely is caused by has_one associations in my model.
Issue
After declaring those associations i started having weird interaction where i'd suddenly get "undefined method
email' for nil:NilClass"` with the following snippet from the error console:
<div class="row">
<div class="column-md-6 column-md-offset-3">
<% @posts.each do |post| %>
email: <%= post.user.email %> <br> # <= source of the issue!
Level: <%= post.level %> <br>
Region: <%= post.region %> <br>
Info: <%= post.description %> <br>
<% if post.user == current_user %>
<%= link_to "Edit", edit_post_path(post) %>
Source of the issue
I had hard time tracking down the bug and trying to reproduce it. I took a quick look at my database in rails console and found out that user_id becomes nil even though the creation of the post from user is successful. I finally managed to reproduce the error by following these steps:
1) Login
2) Create the post
3) Go to "All posts". Post index action renders here all the Posts from every user correctly.
4) While still logged in as the same user, create another post.
5) Go back to "All posts" and I get the aforementioned error.
Post model
class Post < ActiveRecord::Base
validates_presence_of :level, :region, :description
validates :level, length: { maximum: 50 }
belongs_to :user
end
User model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :post
end
Posts controller
class PostsController < ApplicationController
before_action :set_post, only: [:edit, :update]
before_action :authenticate_user!, except: [:index, :show]
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def new
@post = current_user.build_post
end
def create
@post = current_user.build_post(post_params)
if @post.save
flash[:success] = "Post successfully created!"
redirect_to posts_path
else
render 'new'
end
end
def edit
end
def update
#@post = Post.find(post_params)
if @post.update(post_params)
flash[:success] = "Profile updated"
redirect_to posts_path
else
render 'edit'
end
end
def destroy
Post.find(params[:id]).destroy
flash[:success] = "Post deleted"
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:description, :level, :region)
end
def set_post
@post = Post.find(params[:id])
end
end
schema.rb
ActiveRecord::Schema.define(version: 20151209193950) do
create_table "posts", force: :cascade do |t|
t.text "description"
t.string "level"
t.string "region"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
Question
My goal was to have a one post limit for each user. I'm almost certain the has_one association is part of the issue. My question is whether it was a correct way of achieving my goal or should i use has_many association and enforce the limit in some other way?
If has_one is the correct way, how do i fix this issue?
Upvotes: 2
Views: 391
Reputation: 86
In your new and create actions, you call current_user.build_post
, which has a somewhat surprising side-effect in Rails.
Immediately after assigning to or building a has_one association, a query is fired which sets the foreign key on the associated model to nil. There is no way to stop this query from being executed on has_one
associations. If you change the relationship on User
to a has_many, it will work as expected.
Upvotes: 1
Reputation: 1580
Since you mentioned that a user can only have one post you have to check if a user don't have any post before creating. Try like below
def create
@post = current_user.build_post(post_params) unless current_user.post
if @post.save
flash[:success] = "Post successfully created!"
redirect_to posts_path
else
flash[:success] = "You exceeded your limit!"
redirect_to root_path
end
end
and also you should add @benjamin suggestion as well for edit action.
Upvotes: 1
Reputation: 2185
You have to find the correct user again in your edit action of the controller and make it available to the view, otherwise the instance is nil, leading to your error.
Edit_0:
In general, right before the code you have posted, add:
<%= debug params %>
to see which variables you are missing/available in your view.
Upvotes: 2