同时创建和推送关联

时间:2015-05-30 11:58:35

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

我有一个Word模型和一个分类模型。

单词has_and_belongs_to_many类别。 分类has_and_belongs_to_many单词。

现在,我已在控制台中设置并运行所有内容,例如:

Word.create(title: "Testicles")
testicles = Word.first

Category.create(title: "Genitalia")
genitalia = Category.first

testicles.categories << genitalia

testicles.categories
=> "genitalia"

现在我也可以在视图中使用表单来启动和运行,但前提是我在单独的页面上以自己的形式单独创建了类别,并且与Word相同。然后在Word show视图中,我可以创建一个表单来为其分配类别。

但是......我真正想要做的就是在我创建Word时,即在“新词”视图上同时完成所有这些操作。

我在解决如何做到这一点时遇到了很大的问题。我我说我在这个视图中只能有一个表单和一个提交,所以我想我不得不将所有内容从该表单发送到WordsController,并且工作那里有一些魔力,但到底该做什么让我头疼不已。有人可以帮忙吗?

我还没有创建用户模型或设置身份验证,因此在这方面没有障碍。

模型/ word.rb:

class Word < ActiveRecord::Base
  belongs_to :user

  has_and_belongs_to_many :categories

  validates :title, presence: true
end

模型/ category.rb:

class Category < ActiveRecord::Base
    has_and_belongs_to_many :words

    validates :title, presence: true
end

schema.rb:

ActiveRecord::Schema.define(version: 20150529144121) do

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

  create_table "categories_words", id: false, force: :cascade do |t|
    t.integer "category_id"
    t.integer "word_id"
  end

  add_index "categories_words", ["category_id"], name: "index_categories_words_on_category_id"
  add_index "categories_words", ["word_id"], name: "index_categories_words_on_word_id"

  create_table "words", force: :cascade do |t|
    t.string   "title"
    t.text     "description"
    t.integer  "user_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "words", ["user_id"], name: "index_words_on_user_id"

end

在尝试各种form_tag魔法(并且失败严重)后,目前我正在使用此表单:

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

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

  <div class="field">
    <%= f.label :title, 'Word' %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :description, 'Definition' %><br>
    <%= f.text_area :description %>
  </div>
  <div class="field">
    <%= f.label :categories, 'Category' %><br>
    <%= f.text_field :categories %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

在视图中,表单包含:

#<Category::ActiveRecord_Associations_CollectionProxy:0x007f16dd834820>
<类别'框中的

,但可以清除此项并输入您自己的类别。在提交填写了所有字段的表单时,我会收到NoMethodError。

2 个答案:

答案 0 :(得分:2)

所以基本上你想同时创建一个单词和一个类别,然后链接它们。如果经典解决方案(例如使用nested_form宝石)不能完全按照您的意愿行事,那么您可以尝试这种方法。

您真的需要一个自定义视图/控制器,并按照您的建议在控制器中执行业务。我建议您使用简单的form_tag来添加一些字段集。

视图

<%= form_tag your_custom_route_path, :html => {:class => "form-horizontal"} do |form| %> 
    <%= fields_for :word, @word do |f| %>
      <%= f.text_field :title %>
    <% end %>

    <%= fields_for :category, @category do |f| %>
      <%= f.text_field :title %>
    <% end %>
<% end %>

路由

post '/yourRoute', to: 'your_controller#your_action_create', as: 'your_custom_route'

控制器具有一些动作

class YourController < ApplicationController

  # new
  def your_action_new
    @word = Word.new
    @category = Category.new
  end

  # Post
  def your_action_create
    @word = Word.new(word_params)
    @category = Category.new(category_params)
    if @word.save and @category.save
      @word.categories << @category
      @word.save 
    end
  end

  private

  def words_params
    params.require(:word).permit(:title, ...)
  end

  def category_params
    params.require(:category).permit(:title...)
  end

答案 1 :(得分:0)

感谢Cyril DD的帮助,我对此进行了扩展并创建了一个表单和自定义控制器,它将创建一个新的Word,并为其分配一个新的类别和/或用户可以分配现有的类别那个新词。

_form.html.erb:

<%= form_tag(controller: "new_word", action: "create_word_and_category", method: "post") do %>

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

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

  <%= fields_for :word, @word do |word_form| %>
    <div class="field">
      <%= word_form.label(:title, "Word:") %><br>
      <%= word_form.text_field(:title, id: "new_word", required: true) %>
    </div>

    <div class="field">
      <%= word_form.label(:description, "Definition:") %><br>
      <%= word_form.text_area(:description) %>
    </div>
  <% end %>


  <%= fields_for :category, @category do |category_form| %>
    <% if Category.count > 0 %>
      <div class="field">
        <%= category_form.label(:title, "Choose from existing Categories:") %><br>
        <%= category_form.collection_check_boxes(:category_ids, Category.all, :id, :title) %>
      </div>
    <% end %>

    <h4>AND/OR...</h4>

    <div class="field">
      <%= category_form.label(:title, "Create and Use a New Category:") %><br>
      <%= category_form.text_field(:title) %>
    </div>
  <% end %>

  <div class="actions">
    <%= submit_tag("Create") %>
  </div>
<% end %>

new_word_controller.rb(它还没有完全完成,但它已经完成了它在锡上所说的内容):

class NewWordController < ApplicationController

    def create_word_and_category
        @word = Word.new(word_params)

        if @word.save
            (params["category"])["category_ids"].each do |i|
                next if i.to_i == 0
                @word.categories << Category.find(i.to_i) unless @word.categories.include?(Category.find(i.to_i))
            end

            if category_params.include?(:title) && ((params["category"])["title"]) != ""
                @word.categories << Category.new(title: (params["category"])["title"])
            end
        end

        if @word.save
            redirect_to words_path, notice: 'Word was successfully created.'
        else
            redirect_to new_word_path, notice: 'A valid word was not submitted.'
        end
    end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def word_params
      params.require(:word).permit(:title, :description, :user_id)
    end

    def category_params
        params.require(:category).permit(:title, :category_ids, :category_id)
    end

end

在我的routes.rb中:

post 'create_word_and_category' => 'new_word#create_word_and_category'

我打算将控制器代码放入旧的words_controller中,因为我没有看到需要将它放在像这样的单独控制器中。如果有人有任何想法/批评,那很好。如果有人能帮助我为这个控制器代码编写一个rspec控制器测试,那就更好了!我遇到了麻烦。