has_one关联和消失的用户ID

时间:2015-12-11 16:08:05

标签: ruby-on-rails ruby ruby-on-rails-4 associations

我遇到的错误很可能是由我模型中的has_one关联引起的。

问题

在宣布这些关联后,我开始进行奇怪的交互,我突然得到"undefined method电子邮件'为nil:NilClass“`以及来自错误控制台的以下片段:

<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) %>

问题的根源

我很难追踪错误并尝试重现它。我在rails控制台中快速查看了我的数据库,发现即使从用户创建帖子成功,user_id也会变为零。我终于设法通过以下步骤重现错误:

1)登录

2)创建帖子

3)转到“所有帖子”。 Post index action会在这里正确呈现每个用户的所有帖子。

4)虽然仍以同一用户身份登录,但请创建另一个帖子。

5)返回“所有帖子”,我收到上述错误。

发布模型

class Post < ActiveRecord::Base
  validates_presence_of :level, :region, :description
  validates :level, length: { maximum: 50 }

  belongs_to :user
end

用户模型

class User < ActiveRecord::Base  
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_one :post
end

帖子控制器

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

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

问题

我的目标是为每个用户设置一个帖子限制。我几乎可以肯定has_one关联是问题的一部分。我的问题是,这是否是实现我的目标的正确方法,还是我应该使用has_many关联并以其他方式强制执行限制?

如果has_one是正确的方法,我该如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

您必须在控制器的编辑操作中再次找到正确的用户并使其可供视图使用,否则实例 nil ,从而导致您的错误。

<强> Edit_0:
通常,在您发布的代码之前,添加:

<%= debug params %>

查看视图中缺少/可用的变量。

答案 1 :(得分:1)

由于您提到用户只能有一个帖子,您必须在创建之前检查用户是否没有任何帖子。试试如下

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

并且您还应该添加@benjamin建议以及编辑操作。

答案 2 :(得分:1)

在你的新动作和创建动作中,你调用current_user.build_post,这在Rails中有一些令人惊讶的副作用。

在分配或构建has_one关联之后,会立即触发一个查询,该查询将关联模型上的外键设置为nil。无法阻止此查询在has_one关联上执行。如果您将User上的关系更改为has_many,它将按预期工作。

http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_one#737-build-association-deletes-existing-dependent-record