Reputation: 380
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
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