Ask A Question

Notifications

You’re not receiving notifications from this thread.

accepts_nested_attributes_for with a first_or_initialize

george norris asked in General

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?

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 85,376+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.