views:

128

answers:

2

Hello people,

I have two models with a HABTM association, let´s say book and author.

class Book
  has_and_belongs_to_many :authors
end

class Author
  has_and_belongs_to_many :books
end

The author has a set of attributes (e.g. first-name,last-name,age) that can all be blank (see validation).

validates_length_of :first_name, :maximum => 255, :allow_blank => true, :allow_nil => false

In the books_controller, I do the following to append all authors to a book in one step:

@book = Book.new(params[:book])
@book.authors.build(params[:book][:authors].values)

My question: What would be the easiest way to avoid the saving of authors which fields are all blank to prevent too much "noise" in the database?

At the moment, I do the following:

validate :must_have_some_data

def must_have_some_data
  empty = true
  hash = self.attributes
  hash.delete("created_at")
  hash.delete("updated_at")
  hash.each_value do |value|
    empty = false if value.present?
  end

  if (empty)
    errors.add_to_base("Fields do not contain any data.")
  end
end

Maybe there is an more elegant, Rails-like way to do that.

Thanks.

+1  A: 

A little shorter

def must_have_some_data
  hash = self.attributes
  hash.delete("created_at")
  hash.delete("updated_at")
  errors.add_to_base("Fields do not contain any data.") if hash.select{|k,v| !v.blank?}.empty?
end

Actually I think, that you should validate not all attributes, but just specific attributes, which you are expecting to presence

def must_have_some_data
  valid_fields = ['first_name', 'second_name', 'last_name']
  errors.add_to_base("Fields do not contain any data.") if self.attributes.select{|k,v| valid_fields.include? k and !v.blank?}.empty?
end

UPD In this situation you should also check authors fields in controller. So your authors fields must be in separate params group.

def create
  book = Book.new(params[:book])
  params[:authors].each do |author|
    book.authors.build(author) unless author.attributes.each{|k,v| !v.blank?}.empty?
  end
  if book.save
    ...
  end
end
fl00r
Thanks for your help. If you edit your answer, i´ll vote for it again (I accidentally hit the button twice). My problem is still the following:When I do @book.save, the object is not saved if one of the associated authors has no values (because of the validation). This behaviour I want to prevent. Author fields can be empty and should not be saved when empty but the book should. Any idea?
auralbee
ok, I've updated answer
fl00r
Thanks again. With your code I got the following error: undefined method `attributes' for ArrayThis is how I solved it:def create book = Book.new(params[:book]) params[:authors].each do |key,value| book.authors.build(params[:authors][key]) unless params[:authors][key].select{|k,v| !v.blank?}.empty? end if book.save ... endend
auralbee
sorry, I've mistaken: `book.authors.build(author) unless author.attributes.each{|k,v| !v.blank?}.empty?` not authors.attributes... (singular) And actually you should look on accepts_nested_attributes_for as inkdeep suggested. I usually use this method for creating nested resources. But it works only on latest versions of rails
fl00r
+1  A: 

put this in the books model:

validates_associated :authors, :on => :create

Unless you want invalid author objects to be silently ignored but not saved. Then the current solution is one way of solving it.

What version of rails are you using? accepts_nested_attributes_for might be of use in this situation.

inkdeep