【问题标题】:Rails 5 how to edit join model attributes from the parent model's view?Rails 5 如何从父模型的视图编辑连接模型属性?
【发布时间】:2021-01-18 12:22:04
【问题描述】:

我有一个系统,管理员可以在其中创建考试并根据他们销售考试的分支机构定价。例如,考试一在第一分支机构的成本为 5 美元,而在第二分支机构则为 10 美元。

我制作了一个名为 ExamOffering 的连接表,其中包含考试价格,因此每个考试可以在多个分店以不同的价格出售,每个分店可以有多个考试。像这样:

    class Branch < ApplicationRecord
      has_many :exam_offerings
      has_many :exams, through: :exam_offerings
    end

    class Exam < ApplicationRecord
      has_many :exam_offerings
      has_many :branches, through: :exam_offerings
    end

    class ExamOffering < ApplicationRecord
      # this class has a 'price' attribute
      belongs_to :exam
      belongs_to :branch
    end

我需要能够创建一个新的考试并在表单中选择一个分支才能输入价格,但该属性不是考试模型的一部分,而是 ExamOffering 连接表。我尝试了一些方法但失败了(在考试模型中使用accepts_nested_attributes_for :exam_offerings 或遍历所有分支并为控制器中的每个分支创建ExamOfferings)。这样做的“Rails 方式”是什么?我认为这是一个很常见的情况,但我还没有找到适合我的情况的答案。也许这有一个名字,我不知道。

可以这样表述:当我创建一个新的考试时,我希望能够为每个现有的分支输入一个价格。

谢谢。

【问题讨论】:

    标签: ruby-on-rails-5 nested-attributes has-many-through


    【解决方案1】:

    我最终在考试模型中使用了accepts_nested_attributes_for,所以我不必使用其他视图来放置价格,所以它是这样的:

    class Branch < ApplicationRecord
      has_many :exam_offerings
      has_many :exams, through: :exam_offerings
    end
    
    class Exam < ApplicationRecord
      has_many :exam_offerings
      has_many :branches, through: :exam_offerings
      accepts_nested_attributes_for :exam_offerings
    end
    
    class ExamOffering < ApplicationRecord
      # this class has a 'price' attribute
      belongs_to :exam
      belongs_to :branch
    end
    

    重要的部分是视图。我需要在创建考试时将fields_for 与新对象一起使用,但在编辑考试时获取现有的exam_offerings,所以我有以下代码:

    <% if exam.exam_offerings.empty? %>
      <% Branch.all.each do |branch| %>
        <%= form.fields_for :exam_offerings, @exam.exam_offerings.build do |offering| %>
          <div class="field">
            <%= offering.label "Price in #{branch.name}" %>
            <%= offering.text_field :price %>
            <%= offering.hidden_field :branch_id, value: branch.id %>
          </div>
        <% end %>
      <% end %>
    <% else %>
      <%= form.fields_for :exam_offerings do |offering| %>
        <div class="field">
          <%= offering.label "Price in #{offering.object.branch.name}" %>
          <%= offering.text_field :price %>
        </div>
      <% end %>
    <% end %>
    

    【讨论】:

      【解决方案2】:

      您要做的是显式创建连接模型:

      # config/routes.rb
      resources :exam_offerings, only: [:new, :create]
      
      class ExamOffering < ApplicationRecord
        # this class has a 'price' attribute
        belongs_to :exam
        belongs_to :branch
        accepts_nested_attributes_for :exam
        validates_associated :exam
      end
      
      # app/views/exam_offerings/new.html.erb
      <%= form_with(model: @exam_offering) do |f| %>
        <div class="field" %>
          <%= f.label(:branch_id) %>  
          <%= f.collection_select(:branch_id, Branch.all, :id, :name) %>
        </div>
        <div class="field" %>
          <%= f.label(:price) %>  
          <%= f.number_field(:price) %>
        </div>
        <%= f.fields_for(:exam) do |exam_field| %>
          <div class="field" %>
            <%= exam_field.label(:foo) %>  
            <%= exam_field.text_field(:foo) %>
          </div>
        <% end %>
        <%= f.submit %>
      <% end %>
      
      class ExamOfferingsController < ApplicationController
      
        # GET /exam_offerings/new
        def new
          @exam_offering = ExamOffering.new
        end
       
        # POST /exam_offerings
        def create
          @exam_offering = ExamOffering.new(exam_offering_params)
          if @exam_offering.save
             redirect_to @exam_offering, notice: 'Offering Created'
          else
             render :new, notice: 'Oh No!'
          end
        end
      
        private
      
        def exam_offering_params
          params.require(:exam_offering)
                .permit(
                  :branch_id, :price, 
                  exam_attributes: [:foo]
                )
        end
      end
      

      请记住,连接模型并没有什么特别之处——它们并不总是必须隐式创建。很多时候,它们实际上是业务逻辑的重要组成部分,而不仅仅是管道。

      但实际上,如果我正在构建它,我只会创建一个普通的 POST /exams 路由,用户可以在其中创建考试,然后让他们在此之后创建产品。如果需要,使用 AJAX 使其看起来无缝。

      【讨论】:

      • 我明白了,我想避免为连接模型创建另一个视图。在我看来,创造一个东西然后把那个东西的价格放在另一个地方有点奇怪。 AJAX 怎样才能做到这一点?感谢您的回答。
      猜你喜欢
      • 2016-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-29
      • 2013-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多