Ruby on Rails - Nested attributes not working - ruby-on-rails

I have this two simple models:
class Address < ApplicationRecord
belongs_to :community
geocoded_by :full_address
validates :address, :city, :province, :country, presence: :true
validates :postalcode, presence: true, postalcode: true
after_validation :geocode
def full_address
[address, province, postalcode, country].compact.join(', ')
end
end
And
class Community < ApplicationRecord
has_one :address, dependent: :destroy
accepts_nested_attributes_for :address
has_many :community_people, dependent: :destroy
has_many :people, through: :community_people, source: :user
validates :name, :address, :administrators, presence: true
# ...
end
I'm trying to create some stub Communities using a seed.rb:
def self.create_community(administrators: [], residents: [], address: {})
Community.create(
name: Faker::Name.name,
administrators: administrators,
residents: residents,
address_attributes: address
)
#communities += 1
end
But I always get:
ActiveRecord::RecordInvalid: Validation failed: Address community must exist
PS: Also tried using "community.create_address" and other things.
To only way I could get it to work was:
Saving Community (with no address)
Saving Address referencing community_id.
But I had to hack my Model and remove :address from the validates method in community.rb.
So how can I make accepts_nested_attributes_for work?

I think you are using Rails 5. This issue is because of feature change in Rails 5. For more info read this.
You should try adding optional: true to belongs_to relationship. Like this.
class Address < ApplicationRecord
belongs_to :community, optional: true
end

Related

How to validate person's address only if person doesn't belong to company?

In my Rails 5 app I have the following setup:
class Client < ApplicationRecord
has_one :address, :as => :addressable, :dependent => :destroy
accepts_nested_attributes_for :address, :allow_destroy => true
end
class Company < Client
has_many :people
end
class Person < Client
belongs_to :company
end
class Address < ApplicationRecord
belongs_to :addressable, :polymorphic => true
validates :city, :presence => true
validates :postal_code, :presence => true
end
A person can belong to a company but doesn't necessarily have to.
Now I want to validate a person's address only if that person doesn't belong to a company. How can this be done?
There might be other approaches as well, but based on my experience, something like this should work.
validates :address, :presence => true, if: -> {!company}
Hope this helps.
Validations can take either an if or unless argument, which accept a method, proc or string to determine whether or not to run the validation.
In your case:
validates :address, presence: true, unless: :company
Update according to comments
The above only takes care of skipping the validation itself, but due to accepts_nested_attributes_for OP still saw errors when trying to persist a missing address. This solved it:
accepts_nested_attributes_for :address, reject_if: :company_id
Nabin's answer is good but wanted to show another way.
validate :address_is_present_if_no_company
def address_is_present_if_no_company
return if !company_id || address
errors.add(:address, "is blank")
end

Rails 4 - Validates uniqueness with has_many through

I have these three models:
User:
class User < ActiveRecord::Base
validates :name, presence: true
validates :surname, presence: true
validates :email, presence: true, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
has_many :permissions, dependent: :destroy
has_many :stores, through: :permissions
end
Store:
class Store < ActiveRecord::Base
validates :name, presence: true
validates :description, presence: true
has_many :permissions
has_many :users, through: :permissions
end
Permission:
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :store
end
How can i validate the uniqueness of the user.email based on the store.id?
You don't.
You should validate the uniqueness of the users email in User. And validate the uniqueness of the user_id and store_id in Permission.
class User < ApplicationRecord
# ...
validates_uniqueness_of :email
end
class Permission < ApplicationRecord
validates_uniqueness_of :user_id, scope: 'store_id'
end
This allows a user to have permissions for multiple stores - but does not allow duplicates. In general when linking records together use id's - not something like emails.

Rails 4 validation error with has_many through association

I have troubles creating the records in an association with rails 4. It's basically an association between Entry and Author, with a join table in the middle called AuthorsEntry. The schema is the following:
class Entry < ActiveRecord::Base
validates :name, presence: true
validates :from, presence: true
validates :to, presence: true
belongs_to :event
has_many :authors, through: :authors_entry
has_many :authors_entry
class AuthorsEntry < ActiveRecord::Base
validates :author, presence: true
validates :entry, presence: true
belongs_to :author
belongs_to :entry
end
class Author < ActiveRecord::Base
belongs_to :event
has_many :entries, through: :authors_entry
has_many :authors_entry
validates :name, presence: true
validates :event, presence: true
end
In my program_entries_controller.rb I have the following methods:
def create
#program_entry = Entry.new(program_entry_params)
author_ids_params.each do |id|
#program_entry.authors << AuthorsEntry.build(author_id: id)
end
#program_entry.event = #event
if #program_entry.save
flash[:notice] = t(:program_entry_created_successfully)
redirect_to organizer_event_program_entry_path(#event, #program_entry)
else
render :new
end
end
def program_entry_params
params.require(:program_entry).permit(
:name, :abstract, :'from(1i)', :'from(2i)', :'from(3i)',
:'from(4i)', :'from(5i)', :'to(1i)', :'to(2i)', :'to(3i)', :'to(4i)',
:'to(5i)'
)
end
def author_ids_params
params.require(:program_entry).permit(:author_ids => [])
end
I already have the authors saved in my database, the create action should just add a new record for the Entry model and the association (authors_entry) table. But when I try saving the entry it always returns "is_invalid" over authors_entry.
The join table should be named AuthorEntries to follow rails convention.

Rails has_many :through validation

I'm having trouble validating a model from a has_many through association. Below are the relevant models:
Broadcast Model
class Broadcast < ActiveRecord::Base
attr_accessible :content,
:expires,
:user_ids,
:user_id
has_many :users, through: :broadcast_receipts
has_many :broadcast_receipts, dependent: :destroy
validates :user_id, presence: true
validates :content, presence: true
end
Broadcast Receipt Model
class BroadcastReceipt < ActiveRecord::Base
belongs_to :broadcast
belongs_to :user
attr_accessible :user_id, :cleared, :broadcast_id
validates :user_id , presence: true
validates :broadcast_id , presence: true
end
There is also an association with Users that have_many broadcasts receipts through broadcast receipts.
The problem appears to be with the following line:
validates :broadcast_id , presence: true
Whenever I try to create a Broadcast, I get a rollback with no error messages given. However, when removing the above line, everything works as expected.
This looks like a problem with the Broadcast not being saved before the Broadcast Receipts are being created.
Is there any way I'd be able to validate the broadcast_id is set on the receipt model?
This appears to be the same issue discussed here: https://github.com/rails/rails/issues/8828, which was solved by adding :inverse of to the has_many associations to the join model.
There might be some problem in your code structuring. You could give this version a try.
class Broadcast < ActiveRecord::Base
# I assume these are the recipients
has_many :broadcast_receipts, dependent: :destroy
has_many :users, through: :broadcast_receipts
# I assume this is the creator
validates :user_id, :content, presence: true
attr_accessible :content, :expires, :user_id, :user_ids
end
class BroadcastReceipt < ActiveRecord::Base
belongs_to :broadcast
belongs_to :user
# You should be able to validate the presence
# of an associated model directly
validates :user, :broadcast, presence: true
attr_accessible :cleared
end

Failing validations in join model when using has_many :through

My full code can be seen at https://github.com/andyw8/simpleform_examples
I have a join model ProductCategory with the following validations:
validates :product, presence: true
validates :category, presence: true
My Product model has the following associations:
has_many :product_categories
has_many :categories, through: :product_categories
When I try to create a new product with a category, the call to #product.save! in the controller fails with:
Validation failed: Product categories is invalid
When I remove the validations, everything works and the join models are saved correctly.
I'm using strong_parameters but I don't think that should be related to this issue.
This is a "racing condition" in the callback chain.
When you create a product it doesn't have any id before it is saved, therefore there is no product in the scope of ProductCategory.
Product.new(name: "modern times", category_ids:[1, 2]) #=> #<Product id: nil >
At that stage of validation (before saving), ProductCatgory cannot assign any id to it's foreign key product_id.
That's the reason you have association validations : so that the validation happens in the scope of the whole transaction
UPDATE: As said in the comment you still can't ensure presence of a product/category. There's many ways around depending on why you want do this (e.g direct access to ProductCategory through some form)
You can create a flag to have validates :product, presence: true, if: :direct_access?
or if you can only update them: validates :product, presence: true, on: "update"
create your product first (in the products_controller) and add the categories after
... But indeed these are all compromises or workarounds from the simple #product.create(params)
Specifying inverse_of on your joining models has been documented to fix this issue:
https://github.com/rails/rails/issues/6161#issuecomment-6330795
https://github.com/rails/rails/pull/7661#issuecomment-8614206
Simplified Example:
class Product < ActiveRecord::Base
has_many :product_categories, :inverse_of => :product
has_many :categories, through: :product_categories
end
class Category < ActiveRecord::Base
has_many :product_categories, inverse_of: :category
has_many :products, through: :product_categories
end
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
validates :product, presence: true
validates :category, presence: true
end
Product.new(:categories => [Category.new]).valid? # complains that the ProductCategory is invalid without inverse_of specified
Adapted from: https://github.com/rails/rails/issues/8269#issuecomment-12032536
Pretty sure you just need to define your relationships better. I still might have missed some, but hopefully you get the idea.
class Product < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
validates :name, presence: true
validates :description, presence: true
validates :color_scheme, presence: true
belongs_to :color_scheme
has_many :product_categories, inverse_of: :product
has_many :categories, through: :product_categories
end
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
validates_associated :product
validates_associated :category
# TODO work out why this causes ProductsController#create to fail
# validates :product, presence: true
# validates :category, presence: true
end
class Category < ActiveRecord::Base
has_many :product_categories, inverse_of: :category
has_many :products, through: :product_categories
end

Resources