You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails中如何通过单个表单创建两个属性相同的关联模型?

Should I initialize a Goals instance from a Waterusage instance in the Controller or Model?

Great question! The best practice here is to handle this initialization logic in your model layer—here's why, plus how to implement it cleanly:

Why Model Layer?

Putting this logic in the model aligns with Rails' core principles:

  • Single Responsibility: Controllers should focus on handling HTTP requests/responses, not business logic like copying data between related models.
  • Reusability: If you ever need to create a Goals instance from a Waterusage elsewhere (e.g., a background job, admin tool), you can reuse the same method instead of duplicating code.
  • Testability: Model methods are far easier to test in isolation, without needing to mock HTTP requests or controller contexts.
  • Cleaner Code: Encapsulating the copy logic keeps your controller lean and focused on request flow.

Implementation Steps

1. Add a Factory Method to the Goals Model

Create a class method in Goals that takes a Waterusage instance and returns a pre-populated Goals instance:

class Goals < ApplicationRecord
  belongs_to :user
  # Keep all your existing calculation methods (same as Waterusage)

  # Class method to build a Goals instance from a Waterusage
  def self.from_waterusage(waterusage)
    # Copy all attributes except auto-generated fields and user_id (we'll set that separately)
    copied_attributes = waterusage.attributes.except('id', 'created_at', 'updated_at', 'user_id')
    # Build the new Goals instance, linking to the same user
    new(copied_attributes.merge(user: waterusage.user))
  end
end

2. Trigger Creation (Two Options)

Option A: Automatic Creation via Waterusage Callback

If you want the Goals instance to be created automatically whenever a Waterusage is saved, add an after_create callback to Waterusage:

class Waterusage < ApplicationRecord
  belongs_to :user
  before_validation :calculate_totals
  after_create :create_associated_goals

  # Keep all your existing calculation methods...

  private

  def create_associated_goals
    # Use our factory method and save the Goals instance
    Goals.from_waterusage(self).save!
  end
end

This ensures every new Waterusage gets a matching Goals record without extra controller code.

Option B: Explicit Creation in the Controller

If you need more control (e.g., only create Goals under certain conditions), call the factory method directly in your WaterusagesController:

class WaterusagesController < ApplicationController
  def create
    @waterusage = current_user.waterusages.build(waterusage_params)

    if @waterusage.save
      # Build and save the Goals instance
      @goal = Goals.from_waterusage(@waterusage)
      if @goal.save
        redirect_to your_success_path, notice: 'Water usage and goals created successfully!'
      else
        flash[:alert] = 'Water usage saved, but we couldn’t create your goals. Please try again.'
        redirect_to your_fallback_path
      end
    else
      render :new
    end
  end

  # Your existing waterusage_params method...
end

Ensuring Independent Editing

Since Waterusage and Goals are separate models with their own database records, editing Goals attributes later won’t affect the original Waterusage data—this is already handled by Rails’ default behavior. Just make sure your GoalsController has standard edit/update actions to let users modify their goals.

内容的提问来源于stack exchange,提问作者ezweizig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:10:37