Klumbe
Klumbe

Reputation: 380

Enter name in form but save the id as foreign key

I am trying to create a form where one can enter the :name of a foreign model-instance which should be transformed to the :id of that object to be stored as a foreign key.

I have found different ways of doing this, but they all have big disadvantages. For example: I have two models A and B:

class A < ActiveRecord::Base
  belongs_to :b
  validates :b_id, presence: true, inclusion: {in: B.all.map(&:id)}
end

class B < ActiveRecord::Base

end

The _form.html.erb uses this field where one can enter the string:

<%= form_for(@a) do |f| %>
  ...
  <div class="field">
    <%= f.label :b %><br>
    <%= f.text_field :b %>
  </div>
  ...
<% end %>

While the controller for A looks like this:

class AController < ApplicationController
  ...
  before_action :get_a_id, only: [:create, :update]
  ...

  private

    def page_params
      params.require(:a).permit(:name, :b_id, :content)
    end

    def get_a_id
      b_name = params[:a][:b]
      b = B.find_by(name: b_name)
      b_id = b.id unless b.nil?
      params[:a][:b_id] = b_id
    end

end

This works somehow if the entered :name at the text-field has been found in the database. If it is not found, the validation-errors are shown, but the text_field has not been highlighted and it is empty then (instead of containing the previously entered string). I think this is because the field should contain the Object of B instead of the name and this does not exist in case of a wrong string. It isn't a good idea at all to manipulate it like this, I think.

Is there a nice way to achieve that? I don't want a drop-down here because there might be a lot of values in there later. Of course the next step will be to implement some sort of auto-completion, but that should not count here.

In theory there should be a way to associate the text_field with a new B-object but just show the :name. The controller could then try to find a database object with that name and replace the placeholder with it. If it does not find the right object, the validation fails, but the value is still shown (of course the validation needs to be changed then).

Any way to achieve such a thing in a nice way is appreciated. Just let me know if anything is unclear.

EDIT:
What I actually want to achieve:

A form-field that allows to enter a string. That string should be passed to the controller where a search is performed that transforms this string into the id of the object (B), so that it can be used as foreign key. When the Object of class A is saved, a validator should check if the ID is set and exists. If that validation fails, the field (with the string) should be highlighted and the error-message should be shown.

The main-problem is, that the string with the name is not in the model. Just the id is. So if the id is validated (what would be the right approach), the error-messages would not be associated to the field containing the name as string.

Upvotes: 0

Views: 780

Answers (1)

court3nay
court3nay

Reputation: 2365

Update: after getting the full picture:

You want virtual attributes. Then you can do, @a.set_b_name= and it will do the lookup and apply the name, or you can store the name in an instance var and use validations on it. See these links for more detailed info :)

Old answer was:

if B cannot be found, i.e. b.nil?, then you make a new 'b' object that isn't in the database; b = B.new(name: params[:name]).

Upvotes: 1

Related Questions