Initializing active record model using invalid parameters - ruby-on-rails

I have a model that contains a country_id and region_id. The form posts back the name of the country and region (as params[:country] and params[:region]) instead of the ids. This creates a problem when calling 'new'. Is it possible to add code to the model so that the countries and regions can be located by name? I am currently doing it in the controller, but want the code to be more reusable. Is it generally acceptable to override 'new'? This will also be called on 'update'.

You can add accessors in the model for country and region, which can do the lookup and set the appropriate database parameter. So in this example, "country" becomes a settable virtual attribute.
class Location
attr_accessor :country
def country= value
country = Country.find_by_name value
self.country_id = country.id if country.present?
end
end
Disclaimer: code has not been verified. Be sure to review, validate, understand, and enhance with error checking.

Related

Rails 5: insert name in form and save id

I have 4 tables:users, observative_session, observations, celestial_bodies.
Each user has many observative_session and each observative_session has many observations. I already put in the model the associations
So in observative_session I have a foreign_key (user_id) to link it to the user and in observation I have one foreign key (user_id) for the user and a foreign key (observative_session_id) for the observative_session plus another foreign key for the celestial_body (celestial_body_id).
I created a form in which I ask the user to insert the name of a celestial body
<%= f.text_field :celestial_body_id, label: 'Celestial body' %>
but I can't save the string as an id so I need to find the id corresponding to the inserted body and save it instead.
I tryed to define a virtual attribute
def celestial_body_name
CelestialBody.where(' ')
end
def celestial_body_name= (name)
celestyal_body_id = CelestialBody.where(name: celestial_body_name)
end
and then I create the new observation
def create
#observation = #observative_session.observations.build(observation_params)
....
end
but I get the undefined method 'observations' for nil:NilClass
I don't understand if I pass the parameters correctly or not.
Thank you for any help.
You should go with some autocomplete solution as https://github.com/bigtunacan/rails-jquery-autocomplete. You need to think about passing additional hidden field as celestial_body_id to create valid association in controller. I think this part should help you -- https://github.com/bigtunacan/rails-jquery-autocomplete#sending-extra-search-fields.
Of course you can still pass name without autocomplete, but it's bad for UI (you never know whether such category exists and you can even make some typo in it) and it will require additional queries on logic on backend side

query phone number with different format in the model

There's a table named "Person" with attribute id as primary key and phone_number which is from user input so they are formatted in different ways. I need query the id with phone number.
For example, Person.where(:phone_number => 4155332321)
However, the number in the model could be 415-533-2321 or 4155332321. How could I write the query for that?
BTW, I can't change the phone number format in the model. Otherwise, I can convert the phone in the query and model to be the same format.
Thanks
I think you'll need a two-part approach to this.
First, you'll want to also save a "normalized" phone number to the database. This contains no formatting at all -- just numbers. This will require you to add a new column to your database, say, normalized_phone_number and write a before_save callback to store this value.
class Person
before_save :normalize_phone_number
def self.normalize_number(number)
number.gsub(/[^\d]/, '') if string.present?
end
def normalize_phone_number
self.normalized_phone_number = Person.normalize_number(self.phone_number)
end
end
Next, you'll want to write a custom class method to find the person based on a normalized number from user input. Your Person class will now include:
class Person
def self.with_normalized_phone_number(phone_number)
where(normalized_phone_number: normalize_number(phone_number)).first
end
end
You could also write Person.with_normalized_phone_number as an ActiveRecord scope, which would be my preference, but "Using a class method is the preferred way to accept arguments for scopes."

Rails only initialise an object if condition true

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.

Mass assignment with update_attributes does not update collection_singular_ids

I have a simple User model which is associated to many Town objects using a join table (has_and_belongs_to_many). Now I'd like to update the towns belonging to a particular user by assigning a list of comma-separated town ids (coming directly from the form sent as a HTTP POST parameter).
The user object is saved using the following controller code:
#current_object.update_attributes(params[:user])
The params[:user] includes town_ids which is, for example, set to 1,4,6.
Unfortunately, this does not update the user-town associations at all. However, if I do it manually, it works beautifully well:
User.find(:first).town_ids = "1,4,6" # this saves automatically
Could it just be that it is not possible to mass-assign these collection_singular_ids fields?
My user model contains the following:
has_and_belongs_to_many :towns
# necessary for mass-assignment, otherwise it results in an exception:
attr_accessible :town_ids
Any help is greatly appreciated.
You have to pass the town_ids as an array:
User.find(:first).update_attributes(:town_ids=>[1,4,6])
If you pass the ids as a string Rails will attempt to convert the string to an integer:
"1,4,6".to_i # => 1

Ruby on Rails: building a model with an attribute not in the database?

I have a model that sets one of its attributes based off of a form parameter that a user submits. The model is a child resource to a parent resource Home. A hypothetical example is the following:
A class Person has an age attribute. The user submits a birthdate as an HTTP POST parameter and from that, I need to derive the age of the user. So I'd do something like this:
#home.people.build(:name => params[:name], :birthdate => params[:birthdate])
Rails would barf on that for obvious reasons, complaining it doesn't know what the attribute birthdate is. What's the proper way of going about this?
Is it possible to use the build constructor with the supplied solution so that my foreign key relations are also setup properly? If not, what's a better way to work around this problem?
You can use a virtual attribute to accept the parameter from your POST and then calculate the value you want for your column (untested):
class Person < ActiveRecord::Base
def birthdate=(birthdate)
self.age = Time.now - Date.new(birthdate) # [1]
end
end
[1] not the right syntax -- you'd have to parse the date according to the format of your parameter -- but you get the idea

Resources