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."
Related
I have a simple model for students but I want to also have unique student code for each student, I know I can use the student_id but I would like student code to be something like 'STU0001', 'STU0002. How can I implement something like this so that whenever I create a student, it will create the student code and add it to the DB?
Also how can I search students by their ids in this case?
You can do it using before-create hook in your model, something like follow -
class Student < ActiveRecord::Base
# Callbacks
before_create :set_student_id
def set_student_id
self.student_id = "STU" + "%04d" % self.id
end
end
output -
$> student = Student.create()
$> puts student.student_id
$> STU0001
IMHO if you want id to be prefixed as STU just for a nice display purpose then I would suggest not to store that way in database.
Let current implementation of auto incrementing id be untouched.
Just while displaying you can prepend it with STU.
For handy use you can create method to wrap this logic as
def formatted_id # OR friendly_id or whatever you like
"STU#{'%04d' % self.id}"
end
Reasons for not storing prefixed id:
You will have to write a setter
Your column data type will change to string
Your performance will go down as compared to integer column
In future if you decide to change the prefix, then you will have to update existing ids. With my solution you can change all (existing and future) ids formatting anytime by just changing formatting in the method.
Searching depends on exactly what is your usecase. If you can explain that then I can guide you better.
To search students you can write a handy method like below in model (i assume it will be Student or User)
def self.find_by_formatted_id(formatted_id)
self.find(formatted_id.gsub(/^STU/, ''))
end
def self.find_by_formatted_ids(formatted_ids) # formatted_ids will be an array
self.where(id: formatted_ids.map{ |fid| fid.gsub(/^STU/, '') })
end
I have User model. One of it's attributes is phone_number.
class User < ApplicationRecord
attr_accessor :phone_number
end
In form_for #user, I want to have two inputs: dialing_code and number. Then, when the form is submitted, I want phone_number to be equal to these inputs concatenated together. One of the solution would be to make these inputs as attr_accessor in the model.
Is there another solution? I was thinking to make these inputs variables only exist in the template, but I have no idea how to do it.
Rather than use an attr_accessor, you should use a pattern sometimes referred to as a "virtual attribute." The idea is to make a getter that combines the "real" attributes, and a setter that performs logic and sets the related "real" attributes.
In your case, it might look something like this:
def phone_number
"#{dialing_code}-#{number}"
end
def phone_number=(string)
return unless string.respond_to?(:split)
self.dialing_code = string.split('-').first
self.number = string.split('-')[1..-1].join('-')
end
You'll need to tweak this depending on how you want phone_number to be displayed and how you want to parse a phone_number into its component dialing_code and number, but the code above gives you the general idea.
Now you can make a new user using a phone_number and its dialing_code and number will be set as expected:
>> user = User.new(phone_number: '333-444-5555')
>> user.dialing_code
#> "333"
>> user.number
#> "444-5555"
Alternatively, you can set dialing_code and number individually and still get a phone_number as expected:
>> user = User.new(dialing_code: '333', number: '444-5555')
>> user.phone_number
#> "333-444-5555"
Two(or more) choices: Selection of it depends on how you want to maintain the data and query later.
Save dialing_code, number and phone_number as attributes (and data fields).
In template, ask for dialing_code and number, and construct phone_number in a hook.
before_save :construct_phone_number
def construct_phone_number
self.phone_number = "#{dialing_code}-#{number}" if dialing_code.present? && number.present?
end
Retain dialing_code and number as attr_accessor and construct phone_number(database field) in the same fashion as above. In this case, dialing_code and number are sort of throw away attributes. You can be okay with it, if you are sure that user input will always be fine and you don't need to debug any sort of issues with phone_number later.
Note: You will need to permit these virtual attributes in strong parameters.
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
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.
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