Rails协会无法使用属性

时间:2018-05-24 14:42:28

标签: ruby-on-rails nested-attributes model-associations

我在ruby on rails上的关联有问题。 我有这个协会:

class Play < ApplicationRecord
  has_many :program_plays
  has_many :programs, through: :program_plays
end

class ProgramPlay < ApplicationRecord
  belongs_to :program
  belongs_to :play
end

class Program < ApplicationRecord
  has_many :program_plays
  has_many :plays, through: :program_plays
  accepts_nested_attributes_for :plays
end

当我创建计划时,我想关联一个或多个播放。预先创建播放播放有很多属性,其中:title :id 当然。

program_controller:

class Admin::ProgramsController < Admin::AdminController

  def index
    @programs = Program.all
  end

  def new
    @program = Program.new
    @program.plays.build
  end

  def create
    @program = Program.new(program_params)

    if @program.save
      redirect_to admin_programs_path, notice: ''
    else
    end
  end

  private

  def set_program
    @program = Program.find(params[:id])
  end

  def program_params
    params.require(:program).permit(:start_date, plays_attributes: [:title])
  end
end

程序的形式(使用simple_form gem):

<%= simple_form_for [:admin, @program] do |f| %>
  <%= f.error_notification %>

      <%= f.association :plays %>

      <%= f.input :start_date, as: :datetime, minute_step: 15, label: 'Heure' %>

    <%= f.button :submit, 'Poster', class: 'button red-full' %>

<% end %>

现在,当我想在索引中显示程序时,我还想显示相关播放标题。但是当我写道:

program.plays.title

错误告诉我标题不是程序的方法......为什么?我不明白。这种关联是真实的,因为当我放入rails控制台时:

program.plays.to_a
我有:

 Play Load (5.4ms)  SELECT "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1  [["program_id", 21]]
=> []

你能看到问题吗?我很久以前就找它...我很绝望......

更新:

当我执行program.plays.first.title时,在rails控制台中:

  Play Load (0.5ms)  SELECT  "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1 ORDER BY "plays"."id" ASC LIMIT $2  [["program_id", 22], ["LIMIT", 1]]
NoMethodError: undefined method `title' for nil:NilClass

当我这样做时:

program.plays.each do |play|
  play.title
end

结果是:

  Play Load (0.5ms)  SELECT "plays".* FROM "plays" INNER JOIN "program_plays" ON "plays"."id" = "program_plays"."play_id" WHERE "program_plays"."program_id" = $1  [["program_id", 22]]
=> []

在这里我们可以看到 title 是空的......

更新2:

  create_table "plays", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "program_plays", force: :cascade do |t|
    t.bigint "program_id"
    t.bigint "play_id"
    t.index ["play_id"], name: "index_program_plays_on_play_id"
    t.index ["program_id"], name: "index_program_plays_on_program_id"
  end

  create_table "programs", force: :cascade do |t|
    t.datetime "start_date"
  end

也许问题就在这里......

更新3:

> program = Program.new
=> #<Program:0x00000000068ce8e8 id: nil, start_date: nil>
[15] pry(main)> program.id = 1
    => 1
    > program.plays.build(Play.find_by(id: 10))
  Play Load (6.2ms)  SELECT  "plays".* FROM "plays" WHERE "plays"."id" = $1 LIMIT $2  [["id", 10], ["LIMIT", 1]]
ArgumentError: When assigning attributes, you must pass a hash as an argument.
from /home/coeurcoeur/.rvm/gems/ruby-2.5.0/gems/activemodel-5.1.4/lib/active_model/attribute_assignment.rb:28:in `assign_attributes'
> program.save
   (0.1ms)  BEGIN
   (0.3ms)  ROLLBACK
=> false

1 个答案:

答案 0 :(得分:0)

协会看起来很好。你遇到的问题是你正在调用program.plays.title,但请记住,program.plays会有一系列的游戏,而不仅仅是一个游戏,所以你不能直接在那里打电话。

这取决于你想要做什么,例如,如果你想要第一个游戏的标题,你可以做program.plays.first.title

之类的事情。

或者如果您想要打印所有播放标题,您可以执行类似

的操作
program.plays.each do |play|
  play.title
end

我假设程序中充满了代码中的现有程序

你遇到的另一个问题是你不允许控制器上的program_id所以当你创建播放时,由于不允许,因此不保存具有关联的属性program_id,所以只需在你的参数上添加program_id像这样的方法

def program_params
  params.require(:program).permit(:start_date, plays_attributes: [:title, :program_id]) 
end