How to raise error when validation on nested attributes fails - ruby-on-rails-4

Here are 2 models: customer and address. A customer has_one address.
class Customer < ActiveRecord::Base
has_one :address
accepted_nested_attributes_for :address, :allow_destroy => true
end
class Address < ActiveRecord::Base
belongs_to :customer
validates :add_line, :presence => true
end
<% simple_form_for #customer do |f| %>
.....
<%=f.simple_fields_for :address do |builder| %>
<%=render ('address', f: builder) %>
<% end %>
<%end %>
address view
<%=f.input :add_line %>
address is nested attribute in customer. The problem we are having is that if address is modified wrongly (ex, a nil add_line) within customer view, there is no error (#customer.update_attributes in customer controller) popping up. Is there a way setting up the nested attributes in such a way nil add_line will fail the update?

Two things caught my eye with your original post:
One,remember that you need a belongs_to :customer in the Address model.
Two, you need to add a validation in the Customer model
class Customer < ActiveRecord::Base
has_one :address
accepted_nested_attributes_for :address, allow_destroy: true, reject_if: :address_invalid
private
def address_invalid(attributes)
# add custom validation code here ...
end
end

Related

Rails: How to use find_or_create_by in my nested model

Models: I have a Company model that has_many Tasks. Each Task has_one Employee.
Task model:
class Task < ActiveRecord::Base
belongs_to :company
has_one :employee, dependent: :destroy
accepts_nested_attributes_for :employee
validates_associated :noteholder
end
Employee model:
class Employee < ActiveRecord::Base
belongs_to :task
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
Form: I created a nested form for Task that accepts_nested_attributes_for the Employee model. The form has an Employee name field with an autocomplete function that loads all Company.employees.
_form.html.erb form:
<%= simple_form_for(#task) do |f| %>
<%= f.simple_fields_for :employee do |employee| %>
<%= employee.input :name, input_html: { data: { autocomplete_source: #employees.pluck(:name).to_json } } %>
<% end %>
<%= f.button :submit, 'Save' %>
<% end %>
This saves a new record for each employee, regardless of whether or not an employee with that name exists
Desired behaviour:
If the user fills in a new Employee the model should create a new record, whereas if it would provide an existing Employee name, it should not.
My attempt: I thought the find_or_create_by method would be useful here, but have had no success with implementing it.
Question: How do I correctly set up my model sothat the Employee model only saves a new employee if the employee name does not yet exist?

Devise - Nested Attributes - Customer --> Customer_addresses --> Address

I have been trying to create a relationship like the database listed below, using devise to create the Customer table. I have created migrations for the other tables using the ID ing though table-names and ID. My models look like the below. I know I have gone overboard with the has_many relationships, but I have been trying this all day.
Can anyone help or show the correct way to set up this so that I can create, edit and update addresses on the user. I have managed success with simple nested attributes say Customer/ Direct to address but when I place the table in the middle I just cant get the address attributes to show or update.
I guess there is a more complex edit, create , destroy method that needs to be implemented also.
I'm also getting lost with how to allow strong params on this type of nesting as most examples i can find only have it connected to tables that hold the user_id in them and not one that is connected through another table.
cheers in advance for the guidance.
DataModel image here
Tables
customers / Devise table defaults
customer_addresses
id
address_id
address_type_id
customer_id
addresses
id
address xzy Marua Road
other details / Front house
address_type
id
address_type / Home , Business etc
address_type_description / Where you live etc
class Customer < ActiveRecord::Base
has_many :customer_addresses
has_many :addresses
has_many :address_types
accepts_nested_attributes_for :customer_addresses
accepts_nested_attributes_for :address_types
accepts_nested_attributes_for :addresses
end
class UserAddress < ActiveRecord::Base
has_many :customer
has_many :address_types
has_many :addresses
end
class AddressType < ActiveRecord::Base
belongs_to :customer_address
end
class Address < ActiveRecord::Base
belongs_to :user_address
end
<div class="field">
<%= f.fields_for :user_addresses do |ff| %>
<div>
<%= ff.label :address_id %><br />
<%= ff.text_field :address_id %>
<%= ff.fields_for :address do |fff| %>
<%= fff.label :address %><br />
<%= fff.text_field :address %>
<% end %>
<% end %>
</div>
</div>
user controller params
def user_params
params.require(:user).permit(:id, :username, :first_name, :last_name, :email, :password, :password_confirmation,
user_address_attributes:[:user_is, :address_id, :address_type_id],
addresses_attributes:[:id, :address, :other_address_details ],
address_type_attributes:[ :id, :address_type])
end
The associations you defined should have been like the following
class Customer
has_many :customer_addresses
has_many :addresses, :through => :customer_addresses
has_many :address_types, :through => :customer_addresses
end
class Address
has_many :customer_addresses
has_many :customers, :through => :customer_addresses
has_many :address_types, :through => :customer_addresses
end
class AddressType
has_many :customer_addresses
has_many :customers, :through => :customer_addresses
has_many :addresses, :through => :customer_addresses
end
class CustomerAddress
belongs_to :customer
belongs_to :address
belongs_to :address_type
end
class Customer < ActiveRecord::Base
has_many :customer_addresses
accepts_nested_attributes_for :customer_addresses
end
class AddressType
has_many :customer_addresses
end
class CustomerAddress
belongs_to :address_type
belongs_to :customer
belongs_to :address
accepts_nested_attributes_for :address
after_initialize :add_address, unless: 'address.present?'
def add_address
self.build_address
end
end

ActiveAdmin has_many through relationship not updating param Rails

I'm working through creating a has_many: through relationship in active admin. Here are the models as they stand:
class Category < ActiveRecord::Base
has_many :subcategories
end
class Subcategory < ActiveRecord::Base
has_many :product_in_subcategories
has_many :products, through: :product_in_subcategories
accepts_nested_attributes_for :product_in_subcategories, :allow_destroy => true
belongs_to :category
end
class Product < ActiveRecord::Base
has_many :product_in_subcategories
has_many :subcategories, through: :product_in_subcategories
accepts_nested_attributes_for :product_in_subcategories, :allow_destroy => true
end
class ProductInSubcategory < ActiveRecord::Base
belongs_to :product
belongs_to :subcategory
end
In ActiveAdmin I have the permit_params and form like so:
ActiveAdmin.register Product do
# note some params that are product only have been removed for simplicity
permit_params :name, subcategory_id:[:id], product_in_subcategories_attributes: [:id, :subcategory_id, :product_id, :_create, :_update]
form do |f|
f.inputs
f.has_many :product_in_subcategories do |s|
s.input :subcategory_id, :as => :check_boxes, :collection => Subcategory.all
end
f.actions
end
end
The form populates as should, and will save everything except for the subcategory_id. If I enter into the DB a proper subcategory_id the box will show checked on edit.
The messages when saving give:
Unpermitted parameters: subcategory_id
However, it appears it is trying to submit this with the product, for which there isn't a subcategory_id. Any ideas on what I am doing incorrectly here? This is driving me nuts and I've read everything I can find. I'd really like to understand what I'm doing wrong. Thanks.
After much time spent on this one, I couldn't find a suitable solution except for this one, which is actually very nice. It in fact is not much different from my envisioned solution:
The only changes to the above code were made in ActiveAdmin:
ActiveAdmin.register Product do
# note some params that are product only have been removed for simplicity
permit_params :name, product_in_subcategories_attributes: [:id, :subcategory_id, :product_id, :_create, :_update]
form do |f|
f.inputs
f.has_many :product_in_subcategories do |s|
s.input :subcategory_id, :as => :select, :collection => Subcategory.all
end
f.actions
end
end
Very strange how this allows a select box with no issues, but it flips out over check boxes. Nonetheless, I'm happy with the solution.

building nested resources for a form

I have a class User which has sub classes IndividualUser and BusinessUser (through STI). IndividualUser and BusinessUser have a many-to-many association and are linked through a join table called Employees.
Also, a User can have an Account.
I'm trying to set up a nested form, and am struggling to create the required objects.
My models:
class User
has_one :account
end
class IndividualUser < User
has_many :business_users, :through => :employees
has_many :employees
end
class BusinessUser < User
has_many :individual_users, :through => :employees
has_many :employees
end
class Employee < ActiveRecord::Base
belongs_to :business_user
belongs_to :individual_user
end
class Account
belongs_to :user
end
Now, in my controller, I am able to create a new account for an individual user:
# GET /individual_users/new
def new
#individual_user = IndividualUser.new
#individual_user.build_account
end
This works fine.
However, when I try to create a business_user, then individual_users, and then an account for each individual_user:
# GET /business_users/new
def new
#business_user = BusinessUser.new
#business_user.employees.build()
#business_user.employees.each do |employee|
#individual_user = employee.build_individual_user
#individual_user.build_account
end
end
I get the following error:
undefined method `build_account' for nil:NilClass
It appears as though #individual_user is not being created.
I'm not sure why I'm getting this error. Any ideas?
What's really strange, is that if I instead move the code to the view, it appears to work. i.e. in my view:
<% #business_user.employees.each do |employee| %>
<% employee.individual_user.build_scoot_account %>
<% employee.individual_user.scoot_account.id = 2 %>
<%= employee.individual_user.scoot_account.id %>
<% end %>
This displays "2".

how to specify has_many through vs has_many

So I'm trying to figure out how to specify between calling all instances of an object based on the has_many and has_many :through associations. I have users, groups and members. the member model is a pairing of groups and users. the groups also have a user_id in that model. so this will make more sense after you see the code:
user model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :groups
has_many :groups, :through => :members
end
group model
class Group < ActiveRecord::Base
has_many :users, :through => :members
belongs_to :user
end
member model
class Member < ActiveRecord::Base
belongs_to :groups
belongs_to :users
end
Index View
<!--these are the groups I own (the group.user_id is my id) NEEDS EDITING-->
<% #groups.each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
<!--I'm a member of these groups (connected through the member model) NEEDS EDITING'-->
<% current_user.groups.order(:created_at).each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
I'm trying to figure out how to call the different types of associations users and groups in the Index View.
I want to get a list of all the groups that have me listed as admin (group.user_id) and then I want to get a separate list of all the groups that I'm a member of (through the member model).
I know I can query it with a find or where call. However, I was hoping there was a simple way like current_user.groups or something. anyway, thanks!
I would consider calling the 1:N relationship to groups another name. So you would end up with something like
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :owned_groups, class_name: 'Group'
has_many :groups, :through => :members
end
So the index view would look something like:
<% current_user.owned_groups.each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
<% current_user.groups.order(:created_at).each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>