1

我有一个使用全新 pg 数据库的 rails 4 应用程序,创建 20 个用户后我开始收到此错误

ActionView::Template::Error (nil:NilClass 的未定义方法 `gravatar_url')

这是 user.rb 模型:

def gravatar_url
  stripped_email = email.strip
  downcased_email= stripped_email.downcase
  hash = Digest::MD5.hexdigest(downcased_email)

  "http://gravatar.com/avatar/#{hash}?r=x"
end

这是 index.html.erb

<% @statuses.each do |status| %>
<div class="status">
  <div class ="row">
    <div class="span1">
      <%= image_tag status.user.gravatar_url %>
    </div>
    <div class="span7">
      <strong><%= status.user.full_name %></strong>
      <p><%= status.content %></p>
      <div class="meta">
        <%= link_to time_ago_in_words(status.created_at) + " ago" , status %>
        <span class="admin">
          | <%= link_to "Edit", edit_status_path(status) %> |
          <%= link_to "Delete", status, method: :delete, data: { confirm: "Are                     you sure you want to delete this status?"} %>
        </span>
      </div>
    </div>
  </div>
</div>

如果我删除数据库并从头开始使用 rake db:migrate 它工作得很好,但是一旦我达到 20 个用户,事情就开始出错并且应用程序抛出异常。

这是状态控制器

class StatusesController < ApplicationController
  before_action :set_status, only: [:show, :edit, :update, :destroy]

  before_filter :authenticate_user!, only: [:new, :create, :edit, :update]


  # GET /statuses
  # GET /statuses.json
  def index
    @statuses = Status.all
  end

  # GET /statuses/1
  # GET /statuses/1.json
  def show
  end

  # GET /statuses/new
  def new
    @status = Status.new
  end

  # GET /statuses/1/edit
  def edit
  end

  # POST /statuses
  # POST /statuses.json
  def create
    @status = current_user.statuses.new(status_params)

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

  # PATCH/PUT /statuses/1
  # PATCH/PUT /statuses/1.json
  def update
    @status = current_user.statuses.find(params[:id])

    if params[:status] && params[:status].has_key?(:user_id)
      params[:status].delete(:user_id) 
    end  


    respond_to do |format|
      if @status.update(status_params)
        format.html { redirect_to @status, notice: 'Status was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @status.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /statuses/1
  # DELETE /statuses/1.json
  def destroy
    @status.destroy
    respond_to do |format|
      format.html { redirect_to statuses_url }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def status_params
      params.require(:status).permit(:name, :content, :user_id)
    end
end

这是状态类:

class Status < ActiveRecord::Base
  belongs_to :user

  validates :content, presence: true,
                      length:{ minimum: 2 }

  validates :user_id, presence: true

end

这是用户类:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  validates :first_name, presence: true

  validates :last_name, presence: true

  validates :profile_name, presence: true,
                           uniqueness: true,
                           format: {
                             with: /\A[a-zA-Z0-9_-]+\Z/,
                             message: 'must be formatted correctly.'
                           }

  has_many :statuses
 end

和应用程序控制器:

class ApplicationController < ActionController::Base
    before_filter :configure_permitted_parameters, if: :devise_controller?

  protect_from_forgery with: :exception

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :profile_name, 
                                                            :email, :password, :password_confirmation) }

    devise_parameter_sanitizer.for(:statuses) { |u| u.permit(:user_id)}
  end
end
4

2 回答 2

0

至少有一个通过检索到的状态Status.all与用户没有关联。您的视图抛出错误,因为它最终尝试访问gravatarl_url便捷方法 on status.user,但由于状态没有用户,它抛出ActionView::Template::Error (undefined method 'gravatar_url' for nil:NilClass):异常。

想象以下场景:

Status.find(1).user.nil? == false
Status.find(2).user.nil? == false
Status.find(3).user.nil? == true # no associated user
Status.find(4).user.nil? == false

当您分配Status.all@statuses实例变量时,它将包括所有四种状态——包括第三种状态,它没有关联的用户。在您的视图中循环时,当循环@statuses第三个状态时您会收到一个错误:

# first pass
status.user # found

# second pass
status.user # found

# third pass
status.user # NilClass exception is thrown, causing loop to exit

要确定哪些状态没有关联用户,请运行以下 Arel 查询:

Status.joins("left join users on users.status_id = statuses.id").where("users.status_id is null")
#=> [#<Status id: 3>]

要解决该错误,只需将用户与没有用户的状态相关联:

status = Status.find(3)
user = User.create
status.user = user # associates `user` with `status`

Status.joins("left join users on users.status_id = statuses.id").where("users.status_id is null")
#=> [] # all statuses are now associated with a user

更新

要在子模型被销毁时有条件地销毁父节点has_one,您可以在子模型 ( ) 中实现一系列回调,这些回调User将在用户被销毁时执行:

# app/models/user.rb
class User < ActiveRecord::Base
    # ... existing logic

    before_destroy :find_parent
    after_destroy :destroy_orphaned_parent

    def find_parent
         @parent = self.parent
    end

    def destroy_orphaned_parent
        if @parent.children.length == 0
            @parent.destroy
        end  
    end
end
于 2013-07-17T02:42:30.023 回答
0

问题是status.user返回 nil,这意味着没有user_id属性 is nil(或NULL,在数据库中)。

另一种选择可能是用户不再存在,但我相信如果您尝试查找它会引发错误。

于 2013-07-17T02:20:25.617 回答