ruby-on-rails 通过传递主键以外的值来更新Rails关联的正确方法

wztqucjr  于 2023-06-25  发布在  Ruby
关注(0)|答案(1)|浏览(122)

我是一个新的Rails开发者,我试图优化我的应用程序的一部分,但很难找到Rails惯例的方式来实现我想要的。我有一个“位置”模型,其中一个用户可以被指定为默认联系人。这由引用“用户”模型的位置表中的default_contact_id键捕获。在位置编辑视图中,我希望用户能够通过在文本框中输入电子邮件地址来输入所需的default_contact。然而,我在如何实现这一点上遇到了困难--我试着用fields_for来实现,但我最终不想更新关联的用户对象,只想更新关联本身(存储在location中)。我也不想使用下拉列表,即使它会很好地给予user_id,因为可能有几十个或几百个用户可以被分配为默认联系人。
我最终在Location模型中构建了一个attr_accessor来临时获取所需的电子邮件地址,然后向控制器/helper添加逻辑来根据输入更新值。但这是相当冗长的,给了一个双数据库命中,真的不像“Rails方式”。有没有一个更巧妙的方法来实现这一点,我完全错过了?
这是当前的hacked together解决方案:

Location.rb

class Location < ApplicationRecord
  belongs_to :default_contact, class_name: 'User', foreign_key: 'default_contact_id', optional: true

  attr_accessor :default_contact_email
end

views\locations\edit.html.erb

<h1>Update Location</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @location) do |f| %>
      <%= render 'shared/error_messages', object: f.object  %>

      ... other fields ...
    
      <%= f.label :default_contact_email %>
      <%= f.email_field :default_contact_email, :value => @location.default_contact.email, class: 'form-control' %>

      <%= f.submit "Update Location", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

locations_controller.rb

def update
    @location = Location.find(params[:id])
    
    if @location.update(update_location_params)
      set_default_contact(@location)
      flash[:success] = "Location updated"
      redirect_to @location
    else
      flash[:danger] = "Changes were not saved"
      render 'edit', status: :unprocessable_entity
    end
  end

  private
    def update_location_params
      params.require(:location).permit(:name, :business_unit_id, :default_contact_email)
    end

locations_helper.rb

def set_default_contact(loc)
        
    if loc.default_contact_id.nil? or (loc.default_contact && ( loc.default_contact.email != loc.default_contact_email ))
      
      if loc.default_contact_email == nil
        loc.default_contact_id = nil
      elsif
        u = User.find_sole_by(email: loc.default_contact_email)
        loc.default_contact_id = u.id
      end
      loc.save
    end
  end

PS -是的,有数据库的限制,用户的电子邮件必须是唯一的。在用户的方案表中:

t.index ["email"], name: "index_users_on_email", unique: true
qv7cva1a

qv7cva1a1#

您可以删除访问器,并在控制器中应用登录,如下所示。

def update
  @location = Location.find(params[:id])

  @location.default_contact = User.find_by(email: params[:location][:default_contact_email])

  # other stuff for updating
end

您不需要这些额外的检查,因为如果.find_by没有找到用户,它将返回nil(即使email值为nil)

相关问题