【问题标题】:How to create a Child before associating it to its Parent in rails?如何在将 Child 关联到 Rails 中的 Parent 之前创建一个 Child?
【发布时间】:2020-07-22 06:30:24
【问题描述】:

我有类别(父母),其中列出了产品(儿童)

我希望能够在应用程序的任何位置直接从导航栏创建新产品,然后在创建过程中将其分配给类别。

但是,我得到了当前的错误:

NoMethodError in Products#new
Showing /Users/istvanlazar/Mobily/app/views/products/new.html.erb where line #9 raised:

undefined method `products_path' for #<#<Class:0x00007febaa5aec98>:0x00007febae0f9e38>
Did you mean?  product_show_path 

## product_show_path is a custom controller that has nothing to do with this one, 
enabling show and clean redirection from newest_products index bypassing categories nesting.

Extracted source (around line #9):
9  <%= form_for [@product] do |f| %>
10   <div class="form-styling">
11     <div>
12       <%= f.label :name %>

我的应用程序是这样工作的:

模型

class Category < ApplicationRecord
  has_many :products, inverse_of: :category
  accepts_nested_attributes_for :products

  validates :name, presence: true
end


class Product < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :category, inverse_of: :products

  validates :category, presence: true
end

路线

get 'products/new', to: 'products#new', as: 'new_product'

resources :categories, only: [:index, :show, :new, :edit] do
  resources :products, only: [:index, :show, :edit]
  # resources :products, only: [:index, :show, :new, :edit]
end

控制器

class ProductsController < ApplicationController
  before_action :set_category, except: :new

  def index
    @products = Product.all
    @products = policy_scope(@category.products).order(created_at: :desc)
  end

  def show
    @product = Product.find(params[:id])
  end

  def new
    @product = Product.new
    @product.user = current_user
  end

  def create
    @product = Product.new(product_params)
    @product.user = current_user
    if @product.save!
      redirect_to category_product_path(@category, @product), notice: "Product has been successfully added to our database"
    else
      render :new
    end
  end

  private

  def set_category
    @category = Category.find(params[:category_id])
  end

  def product_params
    params.require(:product).permit(:name, :price, :description, :category_id, :user, :id)
  end
end


class CategoriesController < ApplicationController
  def index
    @categories = Category.all    
  end

  def show
    @category = Category.find(params[:id])
  end

  def new
    # Non-existant created in seeds.rb
  end

  def create
    # idem
  end

  def edit
    # idem
  end

  def update
    # idem
  end

  def destroy
    # idem
  end

  private

  def category_params
    params.require(:category).permit(:name, :id)
  end
end

观看次数

# In shared/navbar.html.erb:

<nav>
  <ul>
    <li>Some Link</li>
    <li>Another Link</li>
    <li><%= link_to "Create", new_product_path %></li>
  </ul>
</nav>

# In products/new.html.erb:

<%= form_for [@product] do |f| %>
    <div class="form-styling">
      <div>
        <%= f.label :name %>
        <%= f.text_field :name, required: true, placeholder: "Enter product name" %>

        <%= f.label :price %>
        <%= f.number_field :price, required: true %>
      </div>
      <div>
        <%= f.label :description %>
        <%= f.text_field :description %>
      </div>
      <div>
        <%= f.label :category %>
        <%= f.collection_select :category_id, Category.order(:name), :id, :name, {prompt: 'Select a Category'}, required: true %>
      </div>
      <div>
        <%= f.submit %>
      </div>
    </div>
  <% end %>

你知道哪里出错了吗?还是在将子项分配给父项之前不可能创建子项..?

提前致谢。

最好, 是

【问题讨论】:

  • 你在哪里调用 products_path (抛出的错误?)
  • 几乎无处...看来 form_for 会自动完成
  • 在 232 个文件中搜索“products_path”(整个单词)0 个匹配项
  • @product 的设置是否正确?还有为什么 form_for [@product] 中的数组?
  • @product 应该是 Product.new ,设置在你的控制器新动作中的某处

标签: ruby-on-rails activerecord parent-child nested-form-for accepts-nested-attributes


【解决方案1】:

您尚未定义任何路由来处理新产品表单的 POST。您已经定义了 new_product 路径,但这种安排违反了 Rails 的约定,您没有提供解决方法。

您可以定义另一个自定义路由,例如post 'products', to: 'products#create', as: 'create_new_product' 然后在你的表单中设置它,比如form_for @product, url: create_new_product_path do |f|

但是,您应该考虑更改结构,以便产品路线不会嵌套在类别下。在以这种方式打破常规之前,请三思。

【讨论】:

  • 这似乎起作用了!!
  • 谢谢。但是,作为 Rails 新手,像这样打破常规有什么危险?
  • 我犹豫是否要更改路线,以便产品不会嵌套在类别下,但我的应用程序的其余部分都是基于此构建的,所以我想试一试
  • 当你发现自己在与一种模式作斗争时,把它当作一个警告信号,表明你选择的模式不合适,或者表明你还没有完全内化应该使用模式。
  • 但是该模式简化了相关的 JS 实现。我会检查如何取消嵌套。无论如何,非常感谢您的回答@Jeremy_Weathers,到目前为止它的工作原理是这样的
【解决方案2】:

编辑:我误读了意图,忽略这一点。选择 Jeremy Weather 的答案。

或者在分配给父级之前不可能创建一个子级..?

通过您建立关系和验证的方式:是的,确实如此。一个产品需要一个类别,通过validates :category, presence: true。如果您使用的是 Rails 5+,则默认情况下已经需要关联(这意味着 validates 实际上是多余的)。这意味着如果您尝试创建没有类别的产品,它将失败,并且产品将不会保存到数据库中。

有了这个限制,我建议尝试以下方法:

  • 删除自定义/products/new 路由
    • 删除get 'products/new', to: 'products#new', as: 'new_product'
  • 添加 :new 和 :create 路由以允许嵌套在类别下的产品创建
    • :create:new 添加到:only 数组到嵌套在resources :categories 下的resources :products
  • 将文件views/products/new.html.erb 移动到views/categories/products/new.html.erb(如有必要,在views 中创建一个新的categories 目录)。
  • 在该文件中,更改form_for 以使表单发布到嵌套的 :create 路由:
    • form_for [@category, @product] do |f|
  • 访问 URL /categories/[insert some Category ID here]/products/new,填写一些数据,看看是否可行。

您的 routes.rb 应该如下所示:

resources :categories, only: [:index, :show, :new, :edit] do
  resources :products, only: [:index, :show, :new, :create, :edit]
end

【讨论】:

  • 抱歉,我错过了编辑form_for 的步骤。现在编辑我的答案。
  • 答案已被编辑以包含缺少的步骤,请尝试。
  • 我试过了,但是链接 /categories/2/products/new 将我带到 views/products/new.html.erb 而不是 views/categories/products/new.html.erb
  • 并且没有类别/产品/新的路线
  • 对不起,我想我看错了你的初衷。回去阅读问题,我看到您正在尝试使用从下拉列表中选择的category_id 创建产品。我的错误,我一直在引导你走另一条路。我对你已经有一些嵌套路由感到困惑。我建议您使用 Jeremy Weather 的回答。正如他所说,这并不完全是 Rails 约定,但我认为在您的用例中它是有道理的。
猜你喜欢
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 2018-03-12
  • 2013-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多