【问题标题】:How to manually save a reference object in Rails如何在 Rails 中手动保存引用对象
【发布时间】:2016-11-08 20:26:10
【问题描述】:

编辑:主要问题是当我添加参考字段时,我做了剧院:参考而不是剧院:参考所以该字段没有被标记为外键。一旦我撤消了这些迁移并正确地重新进行了迁移,我就能够完成这项工作。

在我的放映时间控制器中,我试图自动将剧院 ID 设置为拥有用户输入的屏幕的任何剧院,但是当我尝试将其保存为整数或字符串时,我收到错误消息。然而,当我尝试将其保存为剧院对象时,我从控制台收到“未经许可的参数:剧院”,并从 rails 应用程序收到“剧院必须存在”错误。

showtimes_controller:

class ShowtimesController < ApplicationController
  before_action :set_theater, only: [:create, :edit]
  before_action :set_showtime, only: [:show, :edit, :update, :destroy]

  # GET /showtimes
  # GET /showtimes.json
  def index
    @showtimes = Showtime.all
  end

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

  # GET /showtimes/new
  def new
    @showtime = Showtime.new
  end

  # GET /showtimes/1/edit
  def edit
  end

  # POST /showtimes
  # POST /showtimes.json
  def create

    @showtime = Showtime.new(showtime_params)

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

  # PATCH/PUT /showtimes/1
  # PATCH/PUT /showtimes/1.json
  def update
    respond_to do |format|
      if @showtime.update(showtime_params)
        format.html { redirect_to @showtime, notice: 'Showtime was successfully updated.' }
        format.json { render :show, status: :ok, location: @showtime }
      else
        format.html { render :edit }
        format.json { render json: @showtime.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /showtimes/1
  # DELETE /showtimes/1.json
  def destroy
    @showtime.destroy
    respond_to do |format|
      format.html { redirect_to showtimes_url, notice: 'Showtime was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    def set_theater
      screenInfo = Screen.where("id = ?", params[:showtime][:screen])
      params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def showtime_params
      params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater, :screen)
    end
end

放映时间模型:

class Showtime < ApplicationRecord
  belongs_to :movie
  belongs_to :theater
end

放映时间_form

<%= form_for(showtime) do |f| %>
  <% if showtime.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(showtime.errors.count, "error") %> prohibited this showtime from being saved:</h2>

      <ul>
      <% showtime.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :date %>
    <%= f.date_select :date %>
  </div>

  <div class="field">
    <%= f.label :time %>
    <%= f.time_select :time %>
  </div>

  <div class="field">
    <%= f.label :archived %>
    <%= f.check_box :archived %>
  </div>

  <div class="field">
    <%= f.label :movie_id %>
    <%= f.text_field :movie_id %>
  </div>

  <div class="field">
    <%= f.label :screen %>
    <%= f.text_field :screen %>
  </div>

  <%= f.hidden_field :theater, :value => "" %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

尝试保存为整数时出错:

Theater(#70015922237640) expected, got Fixnum(#11723820)

尝试另存为字符串时出错:

Theater(#70015868755420) expected, got String(#11739240)

尝试保存为剧院对象时的日志:

    Started POST "/showtimes" for IP at 2016-11-08 20:22:37 +0000
Processing by ShowtimesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"nENPV5d6YRXdcx3H+Xa9ZypGtyFlaTg+zyENGB10TmW9OyWxLR9Dsl7nDoG9irq+3qApiNA2/oEqL5RZ0SXorA==", "showtime"=>{"date(1i)"=>"2016", "date(2i)"=>"11", "date(3i)"=>"8", "time(1i)"=>"2016", "time(2i)"=>"11", "time(3i)"=>"8", "time(4i)"=>"20", "time(5i)"=>"22", "archived"=>"0", "movie_id"=>"2", "screen"=>"1", "theater"=>""}, "commit"=>"Create Showtime"}
  [1m[36mScreen Load (0.3ms)[0m  [1m[34mSELECT "screens".* FROM "screens" WHERE (id = '1')[0m
  [1m[36mTheater Load (0.2ms)[0m  [1m[34mSELECT  "theaters".* FROM "theaters" WHERE "theaters"."id" = ? LIMIT ?[0m  [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: theater
  [1m[35m (0.1ms)[0m  [1m[36mbegin transaction[0m
  [1m[36mMovie Load (0.2ms)[0m  [1m[34mSELECT  "movies".* FROM "movies" WHERE "movies"."id" = ? LIMIT ?[0m  [["id", 2], ["LIMIT", 1]]
  [1m[35m (0.2ms)[0m  [1m[31mrollback transaction[0m
  Rendering showtimes/new.html.erb within layouts/application
  Rendered showtimes/_form.html.erb (13.6ms)
  Rendered showtimes/new.html.erb within layouts/application (16.4ms)
Completed 200 OK in 323ms (Views: 86.5ms | ActiveRecord: 3.9ms)

我到底要怎么保存这个参数?

【问题讨论】:

  • 为什么你有一个剧院而不是一个剧院ID?
  • Unpermitted parameter: theater 这是需要注意和考虑的重要事项...这意味着您的 permit/require 行中缺少某些内容

标签: ruby-on-rails activerecord rails-activerecord


【解决方案1】:

您是否尝试过将您的对象分配给一个实例变量,并在保存之前分配它?

关于你的 before_action

def set_theater
  @theather = ... # Code to find the theather
end

关于您的创建操作

def create
  @showtime = Showtime.new(showtime_params)
  @showtime.theather = @theather
  ... # Code to save and handle errors
end

【讨论】:

  • 我得到一个ActiveModel::MissingAttributeError (can't write unknown attribute theater_id): 使用这个错误。不知道为什么,因为我服务于整个对象
  • 您是否在使用任何调试 gem,例如 byebug?看看set_theather 方法是否正常工作会很酷。
  • "ActiveModel::MissingAttributeError" 这很有趣......你能给我们更多关于这个错误的上下文吗?查看您的控制台窗口或日志文件(log/development.log)并找到该错误......在它之后应该有一个堆栈跟踪(这是大约 30 行文件名,后面有数字)。复制它并将其粘贴到您的原始问题中。这个堆栈跟踪将准确地告诉我们是哪一行代码导致了这个错误——这对我们找出究竟出了什么问题非常有帮助。
  • 感谢您的帮助,但我想通了。不得不撤消我在剧院:参考而不是剧院:参考的迁移,所以它没有被标记为foreign_key,这使得整个事情变得更加困难
【解决方案2】:

您在代码中的多个位置使用theater 而不是theater_id,并且您需要在所有位置进行更改才能使其正常工作。

首先-您不能在我们的表单中选择theater... html 无法识别theater 的类型并且不会通过-因此您的表单需要通过theater_id 代替(这将是一个它可以愉快地处理的整数。

# eg here make sure it's a theater_id
<%= f.hidden_field :theater_id, :value => @theater.id %>

下一步 - 您的要求/许可可能是引发一些错误的原因 - 您也需要将其设为 Theater_id:

def showtime_params
  params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater_id, :screen)
end

现在您需要使用 screen-info 参数取出剧院 - 但还要记住,有时这可能会出现 nil (因此保护子句总是好的):

def set_theater
  if params[:showtime].present? && params[:showtime][:screen_id].present?
    screen_info = Screen.find(params[:showtime][:screen_id])
    @theater = Theater.find(screenInfo.theater_id)
  end
end

注意:我已将命名方案更新为铁路标准,并删除了您尝试在参数中设置剧院的内容,如下所示:

params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])

我不知道你实际上想用这行代码做什么,但不管它是什么,params 不能那样工作 - 考虑 params 是“传递给我们的一组东西来自用户,然后被丢弃”——我们不使用它来存储我们在控制器中创建的新值。这就是@variables 的用途

你能解释更多你想要做什么,我们会找出正确的方法:)

【讨论】:

  • 感谢您的帮助,但我想通了。不得不撤消我在剧院:参考而不是剧院:参考的迁移,因此它没有被标记为foreign_key,这使得整个事情变得更加困难
猜你喜欢
  • 1970-01-01
  • 2011-01-21
  • 2017-11-04
  • 1970-01-01
  • 2013-01-28
  • 1970-01-01
  • 1970-01-01
  • 2019-07-13
  • 2021-10-18
相关资源
最近更新 更多