Is there an approach to conditionally prevent the creation of an object.
I have a Person class which has_many :contacts. If I try to initialize a Contact without providing an address, then the contact should not be created.
I can do this within a person object:
person.contacts << Contact.new(params[:contact]) if params[:address].present?
But can I do this within the Contact class? i.e. preventing the addition of a new Contact without the if condition above.
The reason for the question is that if I have contact fields on a form each with an address and their own contact_type in a hidden field, then the contact object would be created even if the address field is not populated.
UPDATE
Following further thoughts, the following are other options I have considered, but all have downsides:
remove from the params any contacts which do not include address:
Within the strong params method to iterate the params hash and remove any references to contacts without address params[:person][:contacts_attributes].delete_if { |key, value| value[:address].blank? }. This works, but obviously with a polymorphic model could be DRYer.
Create factory method within the Person and Business objects to define to assess the incoming request for a Person to be created and remove any contacts without address. I assume that this could be made DRY by abstracting into in a module, but this feels rather complex for this scenario.
At present option 1 above is what I am going to go with, but I'd be really interested if there is something that can be done in the Contact object.
This is based on the comment above that you do have a validation on the presence of the address field
Seems like there should be a better way to do this but, does this work for you?
new_contact = Contact.new(params)
person.contacts << new_contact if new_contact.valid?
update:
probably the right way to do this is like this
begin
person.contacts.create! params
rescue ActiveRecord::RecordInvalid => e
end
It should probably be in the controller
In the create method
def create
#contact = Contact.new(params.require(:address).permit(:phone number, contact_type)
<end>
This will not allow a contact to be created without an address but will allow a phone number and a contact type empty or not.
I believe that the answer lies in the following:
accepts_nested_attributes_for :contacts, allow_destroy:true, reject_if: proc { |attributes| attributes[:address].blank? }
If I add the reject_if proc, then any submissions without an address seem to be ignored.
Related
This question RAILS: How to get has_many associations of a model tells how to find all the associations of a Class. I want to do this for an instance of the class. In particular I have a User model, and when I setup a User instance, it has a number of associations e.g. user.profile, user.plans etc. I want to check all the associations have been successfully set up for a particular user instance. How do you do this?
Based on the link you provided you should be able to accomplish what you want by doing this:
User.reflect_on_all_associations.map { |assoc| assoc.name }.each do |assoc|
association_object = user.send assoc
#note this is the user instance not the class.
# do whatever you want with association_object. check if nil?
end
What the code does, it to iterate through the list of association name keys returned the link you provided and then use it to call the "method" (meaning the association) by using send.
Hope that helps
Here's the basic setup:
I have an Order model. An Order has one Address and it accepts_nested_attributes_for :address.
I have a basic order form where I ask a user to input her address. This is handled with nested_fields_for. Everything works great - new addresses are validated and assigned nicely.
However, the problem is that it creates a new Address every time, even if an Address already exists with identical attributes.
I would like to modify the behavior so that if the user-inputted address matches all the attributes for an existing Address, the order assigns the existing Address to itself rather than creating a new one.
The methods I have tried are:
In the controller, try to find an existing Address record with the nested attributes (params[:order][:address_attributes]). If a match exists, delete all the nested attributes and replace them with params[:order][:address_id].
Don't use nested_attributes_for at all and instead override the address= method in the model, then just use the controller to create a new Address based on the parameters and then hand it off to the model.
Both of these solutions seem various degrees of messy. Could somebody please enlighten me on whether this is a controller or model responsibility, and perhaps suggest an elegant way to accomplish this?
Thanks in advance.
Have you tried something like this?
class Order < ActiveRecord::Base
# [..]
before_save :replace_existing_address!
def replace_existing_address!
db_address = Address.where(:city => self.address.city,
:street => self.address.street,
:number => self.address.number).first
self.address = db_address if db_address
end
end
Since I'm asking this as a survey of good ways to do this, I figured I'd offer the solution I'm currently using as well as a basis for comment.
In the Controller:
#new_address = Address.new( params[:order][:address] )
#order.address = new_address
#order.update_attributes( params[:order] )
In the Model:
def address=( address )
return unless address and address.is_a? Address
duplicate_address = Address.where( address_1: address.address_1,
address_2: address.address_2,
[etc. etc.] ).first
if duplicate_address
self.address_id = duplicate_address.id
else
address.save
self.address_id = address.id
end
end
I it's truly a :has_one relationship as you say and not a :has_many, you don't need to explicitly assign the address like you do in your own answer. That's what accepts_nested_attributes is for, after all. This line by itself should work:
#order.update_attributes( params[:order] )
That should create a new address if none exists, and update an existing one.
Your solution may work, but it a) doesn't take advantage of accepts_nested_attributes and b) will leave lots of orphaned addresses in your database.
I have provider and patient models which both are belongs_to contact. On the provider and patient edit forms i use fields_for :contact to render associated contact fields.
The problem is in that i want to use different validation rules for provider.contact and patient.contact objects, i.e. i want to validate presence of contact's first_name in patient edit form, but i don't want to validate presence of first_name in provider edit form.
I tried to add dynamic validation rule in patient model:
validate :contact_first_name_blank
def contact_first_name_blank
errors.add('contact[first_name]', 'can not be blank') if contact.first_name.blank?
end
It adds error message in case of empty first_name field, but it does not hightlights contact[first_name] field.
Please help me resolve this problem, may be there is better way to do such validations.
You're adding errors to the wrong model. The square-bracket notation is only used for naming HTML form elements, not the error structure, which is specified by attribute name as far as I know.
validate :contact_first_name_blank
def contact_first_name_blank
if (contact.first_name.blank?)
errors.add_to_base('Contact first name can not be blank')
contact.errors.add('first_name', 'can not be blank')
end
end
The fields_for call checks for errors on the object passed to it, not any parent objects, as it is unaware of those relationships.
In one of my model objects I have an array of objects.
In the view I created a simple form to add additional objects to the array via a selection box.
In the controller I use the append method to add user selected objects to the array:
def add_adjacents
#site = Site.find(params[:id])
if request.post?
#site.adjacents << Site.find(params[:adjacents])
redirect_to :back
end
end
I added a validation to the model to validate_the uniqueness_of :neighbors but using the append method appears to be bypassing the validation.
Is there a way to force the validation? Or a more appropriate way to add an element to the array so that the validation occurs? Been googling all over for this and going over the books, but can't find anything on this.
Have you tried checking the validity afterwards by calling the ".valid?" method, as shown below?
def add_adjacents
#site = Site.find(params[:id])
#site.neighbors << Site.find(params[:neighbors])
unless #site.valid?
#it's not valid, do something to fix it!
end
end
A couple of comments:
Then only way to guarantee uniqueness is to add a unique constraint on your database. validates_uniqueness_of has it's gotchas when there are many users in the system:
Process 1 checks uniqueness, returns true.
Process 2 checks uniqueness, returns true.
Process 1 saves.
Process 2 saves.
You're in trouble.
Why do you have to test for request.post?? This should be handled by your routes, so in my view it's logic that is fattening your controller unnecessarily. I'd imagine something like the following in config/routes.rb: map.resources :sites, :member => { :add_adjacents => :post }
Need to know more about your associations to figure out how validates_uniqueness_of should play in with this setup...
I think you're looking for this:
#site.adjacents.build params[:adjacents]
the build method will accept an array of attribute hashes. These will be validated along with the parent model at save time.
Since you're validating_uniqueness_of, you might get some weirdness when you are saving multiple conflicting records at the same time, depending on the rails implementation for the save and validation phases of the association.
A hacky workaround would be to unique your params when they come in the door, like so:
#site.adjacents.build params[:adjacents].inject([]) do |okay_group, candidate|
if okay_group.all? { |item| item[:neighbor_id] != candidate[:neighbor_id] }
okay_group << candidate
end
okay_group
end
For extra credit you can factor this operation back into the model.
I have a user model which has multiple addresses. Now for my application in rails, address is not mandatory. So, if someone wants to create a user and enter the address after the user has been created, my application should allow that. My problem is, for Address model I have validations for Address Line 1, City and Postal Code. These fields cannot be blank. When, editing a user, the following code fails:
user.addresses << Address.new
Rails tries to create a new Address and fires an Insert command. This is going to fail because of the validations that is required in the model. The above code doesn't fail if the user is not present in the database. One solution to this problem is to create a separate form_for binding for the edit partial for user. I don't want to do that solution. Is there any solution that allows me to bind an empty Address object for an already existing User object in the database ?
Why attempt to add an empty Address object to the user.addresses collection? I think you could simply do something like:
user.addresses << Address.new unless (conditions)
I unfortunately don't know what your conditions are here, so it could be something like
user.addresses << Address.new unless params[:address].nil?
...although my guess is that you have a real Address object instead of just passing in a blank Address.new...
user.addresses << Address.new
This code isn't going to work anyway if your Address model requires its fields to be set, because you're not supplying a hash to Address.new
If you want to add the address conditionally, you probably want something like this:
if !params[:address].blank?
user.addresses.create(params[:address])
end
or
user.addresses << Address.new(params[:address]) unless params[:address].blank
If you really want to create an "empty" address object for each user (instead of just having users without addresses), you can change your validations so they only fire if the fields are filled out.
Something like this:
class Address < ActiveRecord::Base
validates_presence_of :address1, :if => :non_empty_address?
# etc
private
def non_empty_address?
!address1.blank? || !address2.blank || !city.blank? # etc
end
end
The restful_authentication plugin uses a similar approach to determine if the user's password is required.