基于第三个模型构建嵌套表单问题

时间:2016-06-01 16:23:43

标签: ruby-on-rails ruby-on-rails-4 nested-forms

我想为条目构建一个表单,用户可以预测几个contest_questions。

这意味着我动态地想要获取与推特竞赛相关的竞赛项目,然后让用户注册一个包含每个问题预测的条目。

我该怎么做?截至目前,我的字段未显示,因为<%= f.fields_for :predictions do |prediction| %>未在contest_question块中执行,如果我将其更改为contest_question.fields_for ...,我会收到

  对于#

未定义的方法`fields_for'

tipster_contest.rb

class TipsterContest < ActiveRecord::Base
    belongs_to :bookmaker
    belongs_to :game
    has_many :entries
    has_many :contest_questions

    extend FriendlyId
    friendly_id :name, use: [:slugged, :history]

    enum status: { "Scheduled" => 0, "Open" => 1, "Closed" => 2, "Graded" => 3 }

    scope :published?, -> { where(published: :true) }
end

entry.rb

class Entry < ActiveRecord::Base
    belongs_to :user
    belongs_to :tipster_contest
    has_many :predictions

    accepts_nested_attributes_for :predictions
end

contest_question.rb

class ContestQuestion < ActiveRecord::Base
    belongs_to :tipster_contest
    has_many :predictions
end

prediction.rb

class Prediction < ActiveRecord::Base
    belongs_to :entry
    has_one :contest_question
end

_form.html.erb

<%= form_for(@entry) do |f| %>
  <% @contest.contest_questions.order(name: :asc).each do |contest_question| %>
    <%= f.fields_for :predictions do |prediction| %>
      <div class="field">
        <%= prediction.label contest_question.name %>
        <%= prediction.select(:prediction, @contest.teams) %>
      </div>
      <div class="field">
        <%= prediction.label :wager_amount %>
        <%= prediction.input :wager_amount %>
      </div>
    <% end %>
  <% end %>

  <div class="actions">
    <%= f.submit "Save entry", :class => "btn btn-success" %> <%= link_to 'Back', bets_path, :class => "btn btn-danger" %>
  </div>
<% end %>

我的schema.rb的相关部分,如果需要的话:

create_table "contest_questions", force: true do |t|
    t.integer  "tipster_contest_id"
    t.string   "name"
    t.string   "result"
    t.integer  "min_wager"
    t.integer  "max_wager"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "entries", force: true do |t|
    t.integer  "user_id"
    t.integer  "tipster_contest_id"
    t.integer  "bankroll"
    t.integer  "won"
    t.string   "currency"
    t.boolean  "entry_valid"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "predictions", force: true do |t|
    t.integer  "entry_id"
    t.integer  "contest_question_id"
    t.string   "prediction"
    t.integer  "wager_amount"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "tipster_contests", force: true do |t|
    t.integer  "bookmaker_id"
    t.integer  "game_id"
    t.string   "name"
    t.string   "tournament"
    t.integer  "status",             default: 0
    t.text     "rules"
    t.integer  "prizepool"
    t.string   "currency"
    t.text     "payout_structure"
    t.integer  "tipster_wallet"
    t.string   "teams",              default: [],    array: true
    t.datetime "registration_open"
    t.datetime "registration_close"
    t.boolean  "published",          default: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "logo"
    t.text     "description"
    t.string   "slug"
  end

  add_index "tipster_contests", ["slug"], name: "index_tipster_contests_on_slug", using: :btree

1 个答案:

答案 0 :(得分:0)

管理好解决它,至少让它运转起来。我不知道解决方案在rails标准方面是否有任何好处。无论如何它是:

<强> _form.html.erb

<% title("#{@contest.name}") %>
<% description("Join #{@contest.name} and use your eSports knowledge to win extra cash or in-game skins for games like CS:GO, Dota 2, etc.") %>

<p id="notice"><%= notice %></p>

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

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

  <%= f.hidden_field 'tipster_contest_id', :value => @contest.id %>

  <div class="t_container">
    <div class="t_row">
      <div class="t_left"><b>Placement</b></div>
      <div class="t_middle"><b>Team</b></div>
      <div class="t_right"><b>Wager Amount</b></div>
    </div>

    <%= f.fields_for :predictions, @predictions do |p| %>
      <div class="t_row">
        <div class="t_left"><%= @entry.contest_questions.order(:name => :asc)[p.index].name %></div>
        <div class="t_middle"><%= p.select :prediction, @contest.teams %></div>
        <div class="t_right"><%= p.text_field :wager_amount, label: "Wager amount" %></div>
        <%= p.hidden_field 'contest_question_id', :value => @entry.contest_questions.order(:name => :asc)[p.index].id %>
      </div>
    <% end %>
  </div>

  </br>

  <div class="actions">
    <%= f.submit "Save entry", :class => "btn btn-success" %> <%= link_to 'Back', bets_path, :class => "btn btn-danger" %>
  </div>
<% end %>

<强> entries_controller.rb

class EntriesController < ApplicationController
  before_action :set_entry, only: [:show, :edit, :update, :destroy]

  # GET /entries
  # GET /entries.json
  def index
    @entries_open = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 1})
    @entries_closed = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 2})
    @entries_graded = current_user.entries.includes(:tipster_contest).where(:tipster_contests => {status: 3})
    @contests = TipsterContest.where(status: 0..1).includes(:game)
  end

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

  # GET /entries/new
  def new
    if user_signed_in?
      begin
        @contest = TipsterContest.friendly.find(params[:contest])
        redirect_to tipster_contests_path(@contest), notice: "#{@contest.name} does not accept entries at this point. Registration opens at: #{@contest.registration_open} and closes at: #{@contest.registration_close}." and return true unless @contest.status == "Open"
        @entry = Entry.new(tipster_contest: @contest)
        @predictions = []
        @contest.contest_questions.order(name: :asc).each do |cq|
          @predictions << Prediction.new(entry: @entry.id, contest_question_id: cq.id)
        end
      rescue
        redirect_to tipster_contests_path, notice: 'Could not find a tipster-contest with that ID'
      end
    else
      redirect_to new_user_registration_path, notice: 'You need to sign up to enter the tipster contest!'
    end
  end

  # GET /entries/1/edit
  def edit
    if user_signed_in?
      @contest = @entry.tipster_contest
      @predictions = @entry.predictions
      redirect_to tipster_contests_path, notice: 'This is not your entry to edit' and return true unless @entry.user == current_user
    else
      redirect_to new_user_registration_path, notice: 'You need to sign up to enter or edit a tipster contest entry!'
    end
  end

  # POST /entries
  # POST /entries.json
  def create
    @entry = Entry.new(entry_params)

    @predictions = @entry.predictions

    @contest = @entry.tipster_contest
    @entry.user = current_user
    @entry.won = 0
    @entry.bankroll = @contest.tipster_wallet
    @entry.currency = @contest.currency 

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

  # PATCH/PUT /entries/1
  # PATCH/PUT /entries/1.json
  def update
    @contest = @entry.tipster_contest
    #@predictions = @entry.predictions

    respond_to do |format|
      if @entry.update(entry_params)
        format.html { redirect_to @entry, notice: 'Entry was successfully updated.' }
        format.json { render :show, status: :ok, location: @entry }
      else
        format.html { render :edit }
        format.json { render json: @entry.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /entries/1
  # DELETE /entries/1.json
  def destroy
    @entry.destroy
    respond_to do |format|
      format.html { redirect_to entries_url, notice: 'Entry was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def entry_params
      params[:entry].permit(:id, :tipster_contest_id, predictions_attributes: [:prediction, :wager_amount, :contest_question_id, :id])
    end
end