I have a set of blog post and I want to display them in order of most recent date. How can I call that method in the controller
I tried
#post1 = Post.last
but how would I do the 3 previous to this one?
This:
#post1 = Post.last
doesn't give the last Post in the way you think it does. If you check your logs you'll see something like this:
selector={"$query"=>{}, "$orderby"=>{:_id=>-1}}
so it will be "last in id order", not the last by date. If you want the last by some timestamp then you have to say so:
#post1 = Post.order(:created_at => :desc).first
You'd use some other date or timestamp instead of :created_at if you have one that better matches your intent.
Now, if you want the three previous to that, grab four and throw one away:
other_three = Post.order(:created_at => :desc).limit(4)[1.3] # or any of the ways
Or you could use offset to skip the first one:
other_three = Post.order(:created_at => :desc).offset(1).limit(3)
This should give you what you want:
#post1 = Post.last(4).reverse[0..-2]
Post.last(4).reverse will give you last 4 records in descending order. And, then with this: Post.last(4).reverse[0..-2] you are grabbing all but the last element which is what you want.
If you don't want the descending order, then you can just do this without the reverse part:
#post1 = Post.last(4)[0..-2]
Hope this helps.
Update
You can also do it this way:
#posts = Post.limit(4).order(updated_at: :desc)[1..-1]
Related
I would like to understand why in Rails 4 (4.2.0) I see the following behaviour when manipulating data in a join table:
student.student_courses
returns all associated records of courses for a given user;
but the following will save changes
student.student_courses[0].status = "attending"
student.student_courses[0].save
while this will not
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
Why is that, why are those two working differently, is the first one the correct way to do it ?
student.student_courses[0] and student.student_courses.find(1) are subtly different things.
When you say student.student_courses, you're just building a query in an ActiveRecord::Relation. Once you do something to that query that requires a trip to the database, the data is retrieved. In your case, that something is calling [] or find. When you call []:
student.student_courses[0]
your student will execute the underlying query and stash all the student_courses somewhere. You can see this by looking at:
> student.student_courses[0].object_id
# and again...
> student.student_courses[0].object_id
# same number is printed twice
But if you call find, only one object is retrieved and a new one is retrieved each time:
> student.student_courses.find(1).object_id
# and again...
> student.student_courses.find(1).object_id
# two different numbers are seen
That means that this:
student.student_courses[0].status = "attending"
student.student_courses[0].save
is the same as saying:
c = student.student_courses[0]
c.status = "attending"
c.save
whereas this:
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
is like this:
c1 = student.student_courses.find(1)
c1.status = "attending"
c2 = student.student_courses.find(1)
c2.save
When you use the find version, you're calling status= and save on entirely different objects and since nothing was actually changed in the one that you save, the save doesn't do anything useful.
student_courses is an ActiveRecord::Relation, basically a key => value store. The find method would only work on a model
I have a table where I need to create a new entry or update if one of the columns is inside a range of dates (today).
For example, I have a table of shuttles registration with [name, time] where name represents someone and time is when he would like to take the shuttle on.
Each (name) can register at most once a day.
When someone registers, I would like to update an existing row (of the same day), if exists, or create a new one.
The following query extracts the relevant row, if exists:
Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
Tried to use first_or_create and equivalents but couldn't find the right syntax to apply the range query.
Any idea?
Thanks
Try this:
Shuttle.where(:name => params[:name]).
where(:time => DateTime.now.beginning_of_day..DateTime.now.end_of_day)).
first_or_create(:time => DateTime.now)
Reference: http://guides.rubyonrails.org/v3.2.13/active_record_querying.html#first_or_create
first_or_create doesn't apply in this scenario, because the condition doesn't make sense as the value of an update. It would be like saying "Find a shuttle where the time is any time on this given day, and if you don't find one then create one where the time is any time on this given day". That doesn't make sense: you need a specific time when you create a shuttle.
You need to think about the logic of what you want to do: it's not obvious to me at least.
If I understand you, I would do the following
#shuttle = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day).first
#shuttle.columnname = "new value"
#shuttle.save
If you are doing multiple
#shuttles = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
#shuttles.each do |shuttle|
shuttle.columnname = "new value"
shuttle.save
end
What about something like this?
#shuttle = Shuttle.where(
name: params[:name],
time: DateTime.now.beginning_of_day..DateTime.now.end_of_day
).first_or_initialize do |s|
s.attribute = value
s.save
end
In my app I have invoice numbers like this:
2014.DEV.0001
2014.DEV.0002
2014.TSZ.0003
The three character code is a company code. When a new invoice number needs to be assigned it should look for the last used invoice number for that specific company code and add one to it.
I know the company code, I use a LIKE to search on a partial invoice number like this:
last = Invoice.where("invoice_nr LIKE ?", "#{DateTime.now.year}.#{company_short}.").last
This results in this SQL query:
SELECT "invoices".* FROM "invoices" WHERE "invoices"."account_id" = 1 AND (invoice_nr LIKE '2014.TSZ.') ORDER BY "invoices"."id" DESC LIMIT 1
But unfortunately it doesn't return any results. Any idea to improve this, as searching with LIKE doesn't seem to be correct?
Try wrapping string with % and use lower to convert the query string and result into downcase to avoid any wrong results due to case, try this
last = Invoice.where("lower(invoice_nr) LIKE lower(?)", "%#{DateTime.now.year}.#{company_short}.%").last
You want % for partial match
last = Invoice.where("invoice_nr LIKE ?", "%#{DateTime.now.year}.#{company_short}.%").last
Since you want to match only the left part you need to add one % at the right part of your string
Invoice.where("invoice_nr LIKE ?", "#{DateTime.now.year}.#{company_short}.%").last
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, ........")
The answer to this question should be simple, but I haven't found one through the active record querying guide, other questions here on SO, or through messing around in the Rails console.
I simply want to query the database through the active record querying interface and return the value of a single column of the first or last entry, without having to traverse through the entire table (will explain in a moment).
There is a way to do this with pluck, however the structure of the query messages are as follows:
initial = Message.where("id = ?", some_id).pluck(:value).first
final = Message.where("id = ?", some_id).pluck(:value).last
Unfortunately, this is an extremely inefficient operation as it plucks the value attribute out of every record where there is a match on the provided id, before returning either just the first or last entry. I would like to basically reorder the statements to be something along the lines of:
initial = Message.where("id = ?", some_id).first.pluck(:value)
final = Message.where("id = ?", some_id).last.pluck(:value)
However, I get an NoMethodError explaining there is no method pluck for Message. I've tried to do this various ways:
initial = Message.where("id = ?", some_id).first(:value)
initial = Message.where("id = ?", some_id).first.select(:value)
...
But all return some sort of error. I know returning the
Oops
Somehow part of my question got cut off (including the answer I had at the end) - I'll see if I can find it, but I explored using the select() method, in which I found that select only takes one argument meaning a query string must be built as it cannot take optional arguments like id = ?, some_id, but then I found that just appending a .value (where value is the column attribute that you are trying to get) works, so I switched back to the where method as shown in the answer below.
Answer is in the question, but if you're trying to do something like this:
initial = Message.where("id = ?", some_id).pluck(:value).first
final = Message.where("id = ?", some_id).pluck(:value).last
Change it to this (just reference the column name, in this example it is value, but it could be amount or something):
initial = Message.where("id = ?", some_id).first.value
final = Message.where("id = ?", some_id).last.value