accepts_nested_attributes_for with a first_or_initialize
How do you handle a Model that accepts_nested_attributes_for
for another Model, where you only want first_or_initialize
that child model? At the same time, be able delete the relationship between the two?
For instance if you have a Job
model that has many Tags
.
class Job < ApplicationRecord
has_and_belongs_to_many :Tags
accepts_nested_attributes_for :Tags, allow_destroy: true
validates :tags, length: { minimum: 1, maximum: 2 }
....
and a Tags can have many Jobs
class Tags < ApplicationRecord
has_and_belongs_to_many :jobs
When a user fills out a Job form, they can enter in a couple tags for the job.
Using fields_for
on the form handles this.
If there is already a tag in the database with name Python
then a new tag shouldn't be created called Python
. On the other hand, if this is the first time Python
was used the database should create a new Tag
with name Python
.
At the same time if the user decides to remove Python
tag, and updates the job — the Python
tag is not deleted, only its associated record is deleted. Other Job using the same using the Python
tag are unaffected. Similarly, if the user retypes the text Python
to say Postgres
, it doesn't update the Python
tag, it creates a new one. So again, other Jobs using the same using the Python
tag are unaffected.
How is something like this done?
I took at stab at it, but I've so far only got the uniqueness side of this working. (The deleting of associated record isn't working).
In the Job Model this callback for before_validation
will prevent saving / updating dupes.
before_validation :find_tag
private
def find_tag
repos = self.tags.map do |tag|
Tag.where(name: tag.name).first_or_initialize
end
self.tags = tags
end
But when deleting a Tag
, it breaks, I think because of the
validates :tags, length: { minimum: 1, maximum: 2 }
I.E. A two tags are deleted in the UI, another three are sent — five are present in the params (two with _destroy
, three new ones). The before_validation
callback runs first and causes the update to fail.
Hopefully I made sense describing this issue. Does anyone have any pointers on this type of thing?