简体   繁体   中英

Rails - access foreign_key of belongs_to association in a form

Architecture:
I have two Models: Port and App

  • An App belongs to a port
  • In the App the relation is defined like following:
    belongs_to :port, class_name: 'Port', foreign_key: :port_id, required: true
  • A Port has a number and obviously an id (no, id and number aren't equal)

What I need:
The Port are already existing in a DB. In the create-form from the app I have list with all available ports: <%= f.association :port, collection: Port.available_ports, include_blank: false %> . With this approach, the value of a select Item is equal to the number of the port. But what I need is, that the value isn't the number but the id of the port.

What I tried: I tried to solve my problem by adding following to the f.association: value_method: Port.available_ports . But that didn't work: no implicit conversion of Array into String .

My Question:
Am I on the right track or should I approach my problem in a different way? If so, what would you recommend me to do?

EDIT:
AppController

class JBossAppsController < ApplicationController

  before_action :load_app,
                only: %i[ show destroy ]

  def index
    @apps = JBossApp.all
  end

  def show
  end

  def new
    @app = JBossApp.new
  end

  def create
    @app = JBossApp.new(app_params)
    if @app.save!
      redirect_to root_path
    else
      render 'j_boss_apps/new'
    end
  end

  def destroy
  end

  protected
  def load_app
    @app = JBossApp.find_by(params[:id])
  end

  private
  def app_params
    params.require(:j_boss_app).permit(
        params.require(:j_boss_app).permit(
            :id, :group_id, :port_id, :environment, :status, :fault_count, :request_count, :response_count
        )
    )
  end

end

Port

class Port < ApplicationRecord


# Helper
  def self.available_ports
    unavailable_ports = Port.order(number: :asc).pluck(:number)
    first_port = 8080
    last_port = 65080
    step = 100
    all_ports = (first_port..last_port).step(step).collect { |n| n }
    all_ports - unavailable_ports
  end

# Relations
  has_many :apps,
           class_name: 'JBossApp',
           foreign_key: :port_id,
           dependent: :destroy

  has_one :int_app, ->{
    where(port_id: id, environment: :int)
  }

  has_one :tst_app, ->{
    where(port_id: id, environment: :tst)
  }

  has_one :prd_app, ->{
    where(port_id: id, environment: :prd)
  }

  has_one :dmz_tst_app, ->{
    where(port_id: id, environment: :dmz_tst)
  }

  has_one :dmz_prd_app, ->{
    where(port_id: id, environment: :dmz_prd)
  }

# Validations
  validates :number,
            numericality: {
                greater_than: 0,
                only_integer: true
            },
            presence: true

end

As per my understanding to the Question, you would EITHER need insert the Port number entered by the user to the Ports table and associate it to the app while saving it OR seed all the Ports to Ports table beforehand and then find the ports which do not have their corresponding/associated apps. For that you can change your available_ports method as follows:

def self.available_ports
  Port.includes(:apps).where(apps: {id: nil}).order(ports:{number: :asc})

  # Or similar results can be achieved by the following in Rails 5
  # Port.left_outer_joins(:apps).where(apps: {id: nil}).order(ports:{number: :asc})
end

Now you can use your normal form helper method collection_select as:

<%= f.collection_select :port_id, Port.available_ports, :id, :number, {prompt: "Select a port"}, {class: "form-control"} %>

Please let me know if you need further help with any of the proposed solutions.

def self.available_ports
    unavailable_ports = Port.order(number: :asc).pluck(:number)
    first_port = 8080
    last_port = 65080
    step = 100
    all_ports = (first_port..last_port).step(step).collect { |n| n }
    all_ports = all_ports - unavailable_ports
    all_ports = Port.where(number: all_ports)
 end

in views

<%= f.collection_select :port_id, Port.available_ports, :id, :number, {prompt: "Select a port"}, {class: "form-control"} %>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM