如何在将 Child 关联到 rails 中的 Parent 之前创建它?

How to create a Child before associating it to its Parent in rails?

我有 类别 (Parents),其中列出了 产品 (Children)

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

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

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 %>

你知道哪里出了问题吗?还是 不可能在将 Child 分配给 Parent 之前创建 Child..?

提前致谢。

最好, 我

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

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

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

编辑:误读原意,无视。采纳 Jeremy Weather 的回答。

Or is it impossible to Create a Child before assigning it to a Parent..?

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

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

  • 删除自定义 /products/new 路线
    • 移除get 'products/new', to: 'products#new', as: 'new_product'
  • 添加 :new 和 :create 路由以允许在类别下嵌套创建产品
    • :create:new 添加到嵌套在 resources :categories
    • 下的 :only 数组中
  • 将文件 views/products/new.html.erb 移动到 views/categories/products/new.html.erb(如有必要,在 views 中创建一个新的 categories 目录)。
  • 在该文件中,更改 form_for 以便表单 POST 到嵌套的 :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