How to update attribute that is being validate for uniqueness - ruby-on-rails

Table 'leave_policies' has year, increment and total_entitled fields.
I have written 'validates_uniqueness_of :year' in LeavePolicy model.
update_attributes not working even I am not updating year field.
Please guide for mistake or suggest any better solution.
Class LeavePolicy
validates_uniqueness_of :year
end
#leave_policy is object of LeavePolicy
In table a row with :id = 1 ,year = 1 , increment= 2 , total_entitled = 3. If I update row with id : 1 like
#leave_policy.update_attributes(:total_entitled => 5)
I got the error "year is already taken".

You are sure there is no other row with year of 1, and that the #leave_policy is actually an instance of the row with id 1?
If it were actually a new record, it would fail this way, or if there were any other record with :year == 1.
Validations are run on all attributes, even if you use update_attributes only on some, so if the year is not unique, it will fail even if you are only updating the increment or total_entitled fields.
In general, validates_uniqueness is not a great answer on its own, as race conditions from checking in ruby can prevent it from enforcing uniqueness, so I would also use it with a unique key in the database.
If you want to be able to skip the unique year validation when changing the other fields, you can set the attributes on the model, then call
leave_policy.save(:validate => false) #for rails 3
leave_policy.save(false) #for rails 2
and that will skip all validations, but man, I would think twice.
That seems to defeat the purpose, so I would instead pursue why it is that rails thinks you do not have a unique year - perhaps a look in your db will show there really is a dupe row for the year == 1.

Related

How to sum columns and group by date column in Rails

I have a model with following columns
Charges Model
Date
fee
discount
Data
1/1/15, 1, 1
1/1/15, 2, 1
2/2/15, 3, 3
I have a few named scopes like this_year
I want to do something like Charges.this_year.summed_up
How do I make a named scope for this.
The returned response then should be:
1/1/15, 3, 2
2/2/15, 3, 3
Assuming you have a model with a date field(eg. published_at) and 2 integer fields(eg. fee, discount). You can use "group" method to run GROUP BY on published_at. Then just use sum method if you want only sum of one fields. If you want more than one field, you have to run a select with SQL SUMs inside, to get multiple column sums. Here is an example.
Charge..group(published_at)
.select("published_at, SUM(fee) AS sum_fee, SUM(discount) AS sum_discount")
.order("published_at")
Note: Summarized fields won't show up in rails console return value prompt. But they are there for you to use.
Depending upon what end result you want, you may want to look at .group(:attribute) rather than .group_by:
Charge.group(:date).each do |charge|
charge.where('date = ?', charge.date).sum(:fee)
charge.where('date = ?', charge.date).sum(:discount)
end
I found this approach easier, especially if setting multiple conditions on the data you want to extract from the table.
In any case, I had an accounting model that presented this kind of issue where I needed credit and debit plus type of payment info on a single table and spent a fruitful few hours learning all about group_by before realizing that .group() offered a simple solution.

How to update thousands of records

I have to update an age column based on the value in a date of birth column. There are thousands of records to update.
How do I do this using rails?
Is this the right way to do it?
User.update_all(:age => some_method);
def some_method
age = Date.today.year - dob.year
end
Yes, update_all is the right method but no, you can't do it like this. Your some_method will only get called once to set up a database call (I assume you're persisting to a database). You'll then get an error because dob won't be recognised in the scope of the User class.
You'll need to translate your date logic to SQL functions.
Something like (for mysql):
User.update_all("age = year(now()) -
year(dob) -
(DATE_FORMAT(now(), '%m%d') < DATE_FORMAT(dob, '%m%d'))")
(NB. the date_format stuff is so that you get the right age for people who's birthdays are later in the year than the current date - see this question for more details)
The other option is to use one of the batches functionality in rails.
User.where(some_condition).find_in_batches do |group_of_users|
# some logic
# e.g. group_of_users.update_all(:age => some_logic)
end
This would lock your db for less time. Note that you should pretty much always update with a condition in mind. I can't think of many cases you would want to update an entire table every time something happens.
There are a few options checkout the rails docs or the api.
your query is right.
There are many way to update record in a batch/lot.
But, I think that your query is best. Because it is rails query that will support every condition for all database.
for updating more than one attributes
Model.update_all(:column1 => value1, :column2 => value2, ........)
or
you can use :
Model.update_all("column1 = value1, column2 = value2, ........")

How to create/save this database special calculated field - RoR

I have a database field called sample_code. This field is composed on the following way: sample_id/year/lab_id
The lab_id is fixed, always the same. The year changes accordingly to the current year... The sample_id its incremental (resetting every year and not a database field).
What I want to do is every-time I create a new sample it generates this sample_code for me and saves it along with the rest of the sample fields...
My doubts are:
1 - How can I keep incrementing sample_id if it's not on the database?
2 - How can I reset the sample ID code each year?
3 - Where's the best place to put/call this code? Sample's controller?
Thanks for all the help you can give
If you're not using your database outside of your app, it should be fine to just store the sample_id as its own column. You can then put a method on your model that returns something like:
def sample_code
"#{sample_id}/#{Time.now.year}/<lab_id>"
end
Then you can just increment sample_id each time.
EDIT
Since you need to reset the id to 1 each year and the model is called Sample, you should avoid confusion by calling it something like annual_id instead of sample_id. sample_id would likely be confused with sample.id, which is a completely different thing.
With that change, you should just store the info in three columns on the model to make it easy: annual_id, year, and lab_id. Then for each record you can set:
annual_id = Sample.where(year: Time.now.year).pluck(:annual_id).max.to_i + 1
year = Time.now.year
lab_id = <however you are defining this>
This will use the current year for year and then reset the annual_id to 1 when there are no records because the year has changed (new year will give nil.to_i + 1 => 1).
Then you can just return the format you want for any given data point:
def sample_code
"#{annual_id}/#{year}/#{lab_id}"
end

Tracking Followers Over Time

I want to build functionality in my Rails application that shows follower trends over time.
Currently, my following methodology involves creating and destroying relationship objects - following creates an object with the IDs of the follower and followed and unfollowing deletes that relationship object.
Since the relationship object is deleted upon an unfollow, it's impossible to go back and look at how many followers existed for a followed at any given time.
To solve this, the best solution I can think of is this:
Instead of deleting a relationship object upon unfollowing, create a new object with a negative value of, say, -1. Following would create an object with a positive value of +1. Therefore, adding up the total values for a given pair would yield whether or not they were currently following (1 or 0), while historical trends could also be calculated by adding up the total following values for a given followed.
My question is: Is this the most elegant solution this problem? Is there an easier way to do it? I realize that it's possible to use cron jobs to output a daily number, but that seems like it would duplicate data. Any other suggestions?
Any help would be greatly appreciated!
I would add an active field then instead of deleting the relationship record I would set the record to inactive. Then you'll have to update all of your user facing queries to reflect active = 1. Then you can use the records with active = 0 for reporting purposes. You can also add a deactivated_at field that stores the date that the record was deactivated.
An example scenario would be user 1 follows user 2, follows user 3, follows user 4, un-follows user 2, re-follows user 2, un-follows user 4.
follower_id followed_id active created_at deactivated_at
1 2 0 9/10/2012 9/13/2012
1 3 1 9/10/2012 NULL
1 4 0 9/10/2012 9/17/2012
1 2 1 9/16/2012 NULL
just use paranoia
https://github.com/radar/paranoia
class Relationship < ActiveRecord::Base
acts_as_paranoid
...
end
(if you have a unique index over the two numeric ID columns, remove it, use a plain index)
Then you can have
def currently_following_count(uid)
Relationship.where(:followed_id => uid).count
end
def historical_following_count(uid)
Relationship.unscoped.where(:followed_id => uid).count
end

Increment count before update

I have a count column in my tags table. I wanna increment tag count if a tag is just added to the post while updating, and it's already inside db. I added this to my post model:
before_update :increment_tag
def increment_tag
db_post = Post.find_by_id(self)
self.tags.each do |tag|
unless db_post.tags.include? tag
tag.update_attribute("count", tag.count + 1)
end
end
end
I get the post from db and test if the current tag is already in db, if it is, nothing happens, if it's not there, it should update count field. But for some reason this doesn't work.
You should not have a Count column in a tag. You should in stead set up your models propperly so you could do the following:
db_post.tags.count
If you do it right, you can get this in your tag:
tag.post.tags.count
If your aim is to find how many times the tag is used in a post, in total, you can simply count the instances in the TagToPostColumn (if you got one), which you need to sine this is a many-to-many relation.
Then you do:
TagToPostColumn.where(tag_id: someTag.id).count
count is a standard attribute, and you should never have to keep track of this yourself, unless you actually need a column called count that tracks something other than the models you have in the database. But then it is a good idea to name it something else than count, since it can lead to ambiguous attributes.
Also, i find it very strange that you are doing this:
db_post = Post.find_by_id(self)
Why are you not just using the self parameter, in stead of doing a db lookup to find the post you already have.

Resources