首页 > 解决方案 > 在数据库中传递并保存哈希作为强参数

问题描述

当我创建/更新记录时,我得到以下输出

Processing by ContactsController#create as HTML
  Parameters: {..."custom_fields_attr"=>{"1"=>"testing cft", "2"=>"", "3"=>"", "4"=>""}}, "commit"=>"Create"}

但是,它似乎并没有保存到数据库,当记录保存时,它会影响三个表contactscustom_fields并且custom_field_values,联系人表很好,但是其他两个没有更新,所以哈希的关键是字段fromcustom_fields并且值被存储或更新在custom_field_values

这是表单生成的内容

<div class="field l-margin-sm">
  <label for="contact_custom_fields_attr_1">test</label>
  <input type="text" name="contact[custom_fields_attr][1]" id="contact_custom_fields_attr_1" data-custom_field_id="1" data-mandatory="false">
</div>

我的控制器中有以下内容作为强大的参数

custom_fields_attr: {}

在每个模型中都有这样after_save :save_custom_fields_attr的方法,application_record所以我尝试将它直接添加到模型中,我得到了Unpermitted parameter: 1,所以在我看来,after_save没有被触发。

在我实现强参数之前,我们new. create在控制器中的方法中有以下内容。

def new
  all_params_permitted
  @coontact = Contact.new(params[:contact])
end

all_params_permitted是一种方法,application_controller所以我目前正在尝试将其移至强参数,除哈希contacts_controller外,一切正常。custom_fields_attr

contact.rb(删除了一些不相关的方法)

class Contact < ApplicationRecord
  belongs_to :customer, class_name: 'Customer', foreign_key: 'customer_account_id'
  belongs_to :supplier, class_name: 'Supplier', foreign_key: 'supplier_account_id'
  belongs_to :assignee, class_name: 'User', foreign_key: 'assigned_to'

  has_many :events, dependent: :destroy
  has_many :sales_quotes
  has_many :sales_orders
  has_many :support_tickets
  has_many :estimates

  has_many :leads, -> { where('lead is TRUE') }, class_name: 'Event', foreign_key: 'contact_id'
  has_many :complaints, -> { where('complaint is TRUE') }, class_name: 'Event', foreign_key: 'contact_id'
  has_many :general_notes, -> { order(created_at: :desc) }, as: :reference
  has_many :interaction_notes, -> { order(created_at: :desc) }, foreign_key: 'contact_id'
  has_many :non_complaint_events, -> { where(complaint: nil) }, class_name: 'Event', foreign_key: 'contact_id'

  belongs_to :creator, class_name: 'User', foreign_key: 'created_by'
  belongs_to :updater, class_name: 'User', foreign_key: 'updated_by'

  has_many :opportunity_contacts
  has_many :opportunities, through: :opportunity_contacts
  has_one :anonymisation, as: :entity

  validates_presence_of :name

  validates_format_of :business_email, with: /(?:\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)|(\A\Z)/i
  validates_format_of :private_email, with: /(?:\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)|(\A\Z)/i

  acts_as_taggable_on :tags
  include ActsAsTaggableOnExt::Core

  TYPES = [
    'Customer',
    'Supplier',
    'Customer and Supplier',
    'Customer Prospect',
    'Supplier Prospect',
    'Customer and Supplier Prospect',
    'Other'
  ]

  PROSPECT_STRENGTHS = ['1 (strongest)', 1],
    ['2', 2],
    ['3', 3],
    ['4', 4],
    ['5', 5],
    ['6', 6],
    ['7', 7],
    ['8', 8],
    ['9 (weakest)', 9]

  QUICK_SEARCH_FIELDS = {
    name: {
      column_names: 'contacts.name'
    },
    customers: {
      joins_table: :customer,
      column_names: 'contacts.name',
      filters: { contact_type: 'customer' }
    },
    suppliers: {
      joins_table: :supplier,
      column_names: 'contacts.name',
      filters: { contact_type: 'supplier' }
    },
    tags: {
      tagged_with: true
    }
  }.with_indifferent_access

  after_update :check_for_new_customer

  attr_accessor :additional_validation, :row_number, :skip_this_validation
  after_save :save_custom_fields_attr # what needs to get triggered

  def self.default_controller
    'contacts'
  end

  def default_controller
    self.class.default_controller
  end

  validate :validate

  def validate
    if self.additional_validation
      validate_for_custom_fields
    end
  end
  ...
end

contacts_controller.rb(再次删除了一些降低长度的方法)

class ContactsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_contact, only: [:show, :edit, :update, :remove_contact]
  respond_to :json, :html

  helper_method :sort_column, :sort_direction

  ...

  def index
    if params[:search].present?
      @contacts = Contact.search(params[:qs], params[:search]).order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: 20)
    else
      @contacts = Contact.all.order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: 20)
    end
  end

  def set_section
    @section = 'crm'
  end

  protected :set_section

  ...

  def show
    @contact_complaints = Event.where(complaint: true, contact_id: @contact.id).order('id desc').limit(10)
    @contact_leads = Event.where(lead: true, contact_id: @contact.id).order('id desc').limit(10)
    @contact_events = Event.where(complaint: false, lead: false, contact_id: @contact.id).order('id desc').limit(10)
    @contact_quotes = SalesQuote.where(contact_id: @contact.id).order('id desc').limit(10)
    @contact_sales_orders = SalesOrder.where(contact_id: @contact.id).order('id desc').limit(10)
    @contact_opportunities = @contact.get_contact_opportunities()
    @contact_estimates = Estimate.where(contact_id: @contact.id).order('id desc').limit(10)
    @support_tickets = SupportTicket.where(contact: @contact).limit(10)
    @support_tickets = SupportTicket.where(contact: @contact).limit(10)
  end

  def new
    @contact = Contact.new
    @tags = ActsAsTaggableOn::Tag.all

    if params[:l_tel]
      @contact.phone = params[:l_tel]
    end
    if params[:customer] && customer = Customer.find(params[:customer])
      @contact.customer = customer
    end
    if params[:supplier] && supplier = Supplier.find(params[:supplier])
      @contact.supplier = supplier
    end

    render(action: 'new_remote', layout: false) if request.xhr?
  end

  def edit
    @tags = ActsAsTaggableOn::Tag.all
  end

  def create
    @contact = Contact.new(contact_params)
    @contact.additional_validation = true

    if @contact.save
      if request.xhr?
        render :create_remote, layout: false
      else
        render :index, notice: 'Contact was successfully created.'
      end
    else
      if request.xhr?
        render :new_remote, layout: false
      else
        render :new
      end
    end
  end

  def update
    @contact.additional_validation = true
    @tags = ActsAsTaggableOn::Tag.all

    if @contact.update(contact_params)
      flash[:notice] = 'Contact was successfully updated.'
      redirect_to contacts_path
    else
      render :edit
    end
  end

  ...

  protected

  ...

  def set_contact
    @contact = Contact.find(params[:id])
  end

  private

  def sort_column
    Contact.column_names.include?(params[:sort]) ? params[:sort] : 'id'
  end

  def sort_direction
    %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
  end

  def contact_params
    params.require(:contact).permit(
      :customer_account_id,
      :supplier_account_id,
      :other_information,
      :prospect_strength,
      :business_email,
      :private_email,
      :date_of_birth,
      :second_phone,
      :company_name,
      :contact_type,
      :assigned_to,
      :web_address,
      :created_at,
      :updated_at,
      :salutation,
      :created_by,
      :updated_by,
      :address_1,
      :address_2,
      :address_3,
      :address_4,
      :job_title,
      :postcode,
      :obsolete,
      :mobile,
      :spouse,
      :phone,
      :name,
      :url,

      tag_list: [],
      custom_fields_attr: {}
    )
  end
end

如上所述的方法application_record是这个

def save_custom_fields_attr
    if @posted_custom_fields
      values_h = self.custom_field_values_non_assoc_h

      context = nil
      context = self.get_custom_field_record_type_context if self.respond_to?('get_custom_field_record_type_context', true)

      self.class.custom_fields(context).each do |cf|
        posted_value = @posted_custom_fields[cf.id.to_s]

        if cf.input_type == 'time' && posted_value.respond_to?('strip', true)
          posted_value = posted_value.strip
        end

        cfv = values_h[cf.id.to_s]

        if !cfv && posted_value.present?
          cfv = CustomFieldValue.new({
             custom_field_id: cf.id,
             record_type: self.custom_field_record_type,
             record_id: self.id
          })
        end

        if posted_value.present?
          cfv.value = posted_value
          cfv.save
        elsif cfv
          cfv.destroy
        end
      end
    end
  end

标签: rubyformshashruby-on-rails-5

解决方案


推荐阅读