Why do I get ActionView::Template::Error: undefined method `name' for nil:NilClass on Heroku but not locally

痴心易碎 提交于 2020-01-02 08:25:10

问题


I have a Rails 4 app running SQLite locally and PostgreSQL on Heroku.

I am having problems with the index action of my PostController. I get the above error, but only on Heroku.

I have double checked all the code and run all migrations and restarted the Heroku server. I have also double checked that the migrations are identical both locally and on Heroku. The issue seems to be specifically related to accessing data in the User model through the Post model. For instance, I've tried to access other attributes such as email through the rails console on Heroku but I get the same undefined method error. So I'm pretty sure it has something to do with the table join.

Also to clarify, I do have data in the production DB

Here's my PostsController action:

class PostsController < ApplicationController
  before_filter :authenticate_user!, except: [:index, :show]

  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # GET /posts
  # GET /posts.json
  def index
    @posts = Post.all
  end
end

My Post model:

class Post < ActiveRecord::Base

    belongs_to :user
end

My code in the User model:

class User < ActiveRecord::Base

  rolify

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
  :recoverable, :rememberable, :trackable, :validatable, :confirmable

  belongs_to :qualification
  has_many :posts

  validates :phone, presence: true, format: { with: /\d{3}.\d{3}.\d{4}/, message: "Must be in 555.555.5555 format" }
  validates :name, presence: true
  validates :email, presence: true
  validates :acknowledgement, presence: true

  # new function to set the password without knowing the current password used in our confirmation controller. 
  def attempt_set_password(params)
    p = {}
    p[:password] = params[:password]
    p[:password_confirmation] = params[:password_confirmation]
    update_attributes(p)
  end

  # new function to return whether a password has been set
  def has_no_password?
    self.encrypted_password.blank?
  end

  # new function to provide access to protected method unless_confirmed
  def only_if_unconfirmed
    pending_any_confirmation {yield}
  end

  def password_required?
  # Password is required if it is being set, but not for new records
  if !persisted? 
    false
  else
    !password.nil? || !password_confirmation.nil?
  end
end

end

And my index.html.erb file:

<% @posts.each do |post| %>
<h2 class='subheader'><%= link_to post.title, post %></h2>
<h6>written by <%= post.user.name %> on <%= post.created_at.to_s(:long) %></h6>
<div><%= post.body.html_safe %></div>
<% if user_signed_in? %>
<% if current_user.has_role? :admin %>
<div><%= link_to 'Edit', edit_post_path(post) %></div>
<div><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %>  </div>
<% end %>
<% end %>
<% end %>
<br>

<%= link_to 'New Post', new_post_path %>

The output from my Heroku log:

2013-12-19T06:31:13.280899+00:00 app[web.1]: Started GET "/posts" for 64.114.175.126 at     2013-12-19 06:31:13 +0000
2013-12-19T06:31:13.280899+00:00 app[web.1]: Started GET "/posts" for 64.114.175.126 at  2013-12-19 06:31:13 +0000
2013-12-19T06:31:13.283663+00:00 app[web.1]: Processing by PostsController#index as HTML
2013-12-19T06:31:13.283663+00:00 app[web.1]: Processing by PostsController#index as HTML
2013-12-19T06:31:13.292344+00:00 app[web.1]:   Rendered posts/index.html.erb within layouts/application (6.9ms)
2013-12-19T06:31:13.292553+00:00 app[web.1]: Completed 500 Internal Server Error in 9ms
2013-12-19T06:31:13.292344+00:00 app[web.1]:   Rendered posts/index.html.erb within layouts/application (6.9ms)
2013-12-19T06:31:13.292553+00:00 app[web.1]: Completed 500 Internal Server Error in 9ms
2013-12-19T06:31:13.295581+00:00 app[web.1]:
2013-12-19T06:31:13.295581+00:00 app[web.1]:     6: <div><%= post.body.html_safe %></div>
2013-12-19T06:31:13.295581+00:00 app[web.1]:     3: <% @posts.each do |post| %>
2013-12-19T06:31:13.295581+00:00 app[web.1]:     7: <% if user_signed_in? %>
2013-12-19T06:31:13.295581+00:00 app[web.1]:     2:
2013-12-19T06:31:13.295581+00:00 app[web.1]:     4: <h2 class='subheader'><%= link_to post.title, post %></h2>
2013-12-19T06:31:13.295581+00:00 app[web.1]: ActionView::Template::Error (undefined method `name' for nil:NilClass):
2013-12-19T06:31:13.295581+00:00 app[web.1]:     5: <h6>written by <%= post.user.name %> on <%= post.created_at.to_s(:long) %></h6>
2013-12-19T06:31:13.295778+00:00 app[web.1]:   app/views/posts/index.html.erb:3:in `_app_views_posts_index_html_erb__338969566965495969_70155247178440'
2013-12-19T06:31:13.295778+00:00 app[web.1]:     2:
2013-12-19T06:31:13.295778+00:00 app[web.1]:
2013-12-19T06:31:13.295778+00:00 app[web.1]: ActionView::Template::Error (undefined method `name' for nil:NilClass):
2013-12-19T06:31:13.295778+00:00 app[web.1]:
2013-12-19T06:31:13.295778+00:00 app[web.1]:     3: <% @posts.each do |post| %>
2013-12-19T06:31:13.295778+00:00 app[web.1]:
2013-12-19T06:31:13.295581+00:00 app[web.1]:     8: <% if current_user.has_role? :admin %>
2013-12-19T06:31:13.295581+00:00 app[web.1]:   app/views/posts/index.html.erb:5:in `block in _app_views_posts_index_html_erb__338969566965495969_70155247178440'
2013-12-19T06:31:13.295778+00:00 app[web.1]:     5: <h6>written by <%= post.user.name %> on <%= post.created_at.to_s(:long) %></h6>
2013-12-19T06:31:13.295778+00:00 app[web.1]:     6: <div><%= post.body.html_safe %></div>
2013-12-19T06:31:13.295947+00:00 app[web.1]:     7: <% if user_signed_in? %>
2013-12-19T06:31:13.295947+00:00 app[web.1]:     8: <% if current_user.has_role? :admin %>
2013-12-19T06:31:13.295947+00:00 app[web.1]:   app/views/posts/index.html.erb:5:in `block in _app_views_posts_index_html_erb__338969566965495969_70155247178440'
2013-12-19T06:31:13.295947+00:00 app[web.1]:   app/views/posts/index.html.erb:3:in `_app_views_posts_index_html_erb__338969566965495969_70155247178440'
2013-12-19T06:31:13.295778+00:00 app[web.1]:     4: <h2 class='subheader'><%= link_to post.title, post %></h2>
2013-12-19T06:31:13.295947+00:00 app[web.1]:
2013-12-19T06:31:13.295947+00:00 app[web.1]:
2013-12-19T06:31:13.303637+00:00 heroku[router]: at=info method=GET path=/posts host=www.kettlecreekventure.com fwd="64.114.175.126" dyno=web.1 connect=8ms service=25ms status=500 bytes=1266
2013-12-19T06:31:14.630725+00:00 heroku[router]: at=info method=GET path=/favicon.ico host=www.kettlecreekventure.com fwd="64.114.175.126" dyno=web.1 connect=1ms service=4ms status=200 bytes=0
2013-12-19T06:31:22.445573+00:00 heroku[run.7634]: Process exited with status 0
2013-12-19T06:31:22.459444+00:00 heroku[run.7634]: State changed from up to complete

Again, the code works perfectly on my local machine but fails with the above error after I have deployed to Heroku. There error is in the index.html.erb file when the app tries to reference post.user.name. I'm pulling my hair out trying to figure it out. I would appreciate if anyone can see where my problem is. What am I doing wrong?


回答1:


According to your update I will propose this: Every time you delete a user who has posts and then try to list their posts <%= post.user.name %> will return nil because this post no longer has a user to refer to. You can either use the

has_many :posts, dependent: :destroy 

in the User model as I suggested above, or you add some functionality to the deleting of users to prevent errors when trying to view their posts. Some choices are:

When a user is deleted you can just delete everything but their name and id in that row of the user table, that will allow you to have <%= post.user.name %> return a value.

When a user is deleted you can delete everything in their User table info and add "-user-deleted" after it kind of like what Tumblr does when a user deactivates their account and their name shows up in a post on some other Tumblr.

You can create a User in the DB called "user-deleted" and have that user inherit every post from a user who is deleted. In the User controller add something in your delete action to change the user_id field in their posts to the "user-deleted" account.

Or finally you could just check to see if post.user returns a value, and if not just print something like "The user no longer exists in the system"

This is all dependent of course on if you even want to allow users to be deleted but still keep their posts. That's certainly a likely case if this is more than just a class project or something you are doing just to learn Rails. Hope this helps. It certainly got me thinking about the pitfalls in allowing users to delete their profiles without having something in place to clean up after them.




回答2:


It looks like I may have had some corrupt data in the Heroku DB. I ended up deleting all existing posts via the rails console. Now everything is working fine!

I ran @posts = Post.all @posts.each do |post| post.destroy end

Then I went ahead and entered new posts without any problem. The issue was that I had deleted a user but not the corresponding posts



来源:https://stackoverflow.com/questions/20674647/why-do-i-get-actionviewtemplateerror-undefined-method-name-for-nilnilcla

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!