Noobie activerecord find query


#1

In my webapp a todolist has many todo items. I am trying to display
lists that are completed. By incomplete I mean that all the items in
that list are done (booolean = true). I hope to have a list of active
lists and complete lists on one page.
I’m no ruby expert by any means so I think I have to ‘pipe’ some results
into another query??

Can you help please?


#2

So you want to populate an instance variable with all the items which
are
done, basically. Here we go:

@todos = Todo.find(:all, :conditions => ‘done = Y’)

(or whatever the flag for done is).

Alternatively, you could use

@todos = Todo.find_all_by_done(‘Y’)

but the former will probably get you into better habits.

It’s not a Ruby problem at all, it’s basic database stuff. There’s no
need
to “pipe” anything to a second query, it’s a basic WHERE clause.

How much do you know about development, and SQL in particular? You might
find Rails pretty rough going if this is tricky, just as a warning.

t.


#3

Tom A. wrote:

So you want to populate an instance variable with all the items which
are
done, basically. Here we go:

@todos = Todo.find(:all, :conditions => ‘done = Y’)

(or whatever the flag for done is).

Alternatively, you could use

@todos = Todo.find_all_by_done(‘Y’)

but the former will probably get you into better habits.

It’s not a Ruby problem at all, it’s basic database stuff. There’s no
need
to “pipe” anything to a second query, it’s a basic WHERE clause.

How much do you know about development, and SQL in particular? You might
find Rails pretty rough going if this is tricky, just as a warning.

t.

Thanks, sorry probably did not explain it correctly. My todo_list Has
Manay todo_items. each item has a done flag (boolean). A todo_list
becomes complete when all of its todo_items are marked as done,
otherwise it is not done. The todo_list does not have a done field in
the database.


#4

OK, try pasting the following into your Todo_list model (todo_list.rb)
at the top

has_many :completed_items,
:class_name => ‘Todo_item’,
:conditions => ‘done is not null’

def completed?
if self.todo_items == self.completed_items
true
else
false
end
end

Then you’ll find that Todo_list.completed? should return ‘true’ if all
the items are completed, and false if not. So now you can seperate out
completed and uncompleted lists

I think that’ll work, anyhow. If it does, do you understand why?

(Hope you didn’t think I was condescending last time).


#5

Tom A. wrote:

OK, try pasting the following into your Todo_list model (todo_list.rb)
at the top

has_many :completed_items,
:class_name => ‘Todo_item’,
:conditions => ‘done is not null’

def completed?
if self.todo_items == self.completed_items
true
else
false
end
end

Then you’ll find that Todo_list.completed? should return ‘true’ if all
the items are completed, and false if not. So now you can seperate out
completed and uncompleted lists

I think that’ll work, anyhow. If it does, do you understand why?

(Hope you didn’t think I was condescending last time).

Thanks for the help. It is very similar to what I have in the model
already:
def complete?
todo_items.each do |todoitem|
return false if not taskitem.done?
end
true
end

What I was trying to do was get a has of each like this:

@active_lists = TodoList.find(:all,

@completed_lists = TodoList.find(:all,

The only way I can guess to do it it to return all the todolists then
ittterate over them one by one and add them to an 2 new arrays. Seems a
little complex. I need two variables because of the way the view is
listing them out.


#6

then re iterate over the array of results ordering them in date order.
This would be for each project. Similar to basecamps dashboard (in
function anyway). This is the next hurdle! Would your suggestion here
help as well?

OK. So one thing to consider is Single Table Inheritance; if the
“items” users are fairly similar, you could store them in a table
called “items” and then subclass items, ie
class Todo < Item

This is also in the Agile book, and might be the best way of doing
things. Alternatively, make a method on the User class called
“top_ten” that grabs the top ten from each type of item (the most
you’ll ever need), rams them into an array, sort the array by
created_at desc, and then throw away all but the first ten.
Cumbersome, but it’ll work.

But yeah, I’d consider STI, before the cumbersome way.

Also: do consider cacheing. Slow queries might not be too much of a
burn if they’re only being made once every half hour, and then when
anything gets saved/altered…


#7

Aha.

OK, try this: you could set a new field in your database/model called
“done”. Then what you do is you have an ActiveRecord observer watch
the model, and every time one of its Todo_items gets marked as done,
the observer checks if they’re all done, and if they’re all done, it
sets Todo_list.done to be true. If any of them change status, it
unsets the done status on the list.

Then you CAN just say @active_list = TodoList.find_all(:conditions =>
“done is not null”).

You could either use an observer or a callback on before_save. Have a
look in the agile book or the API documentation - off the top of my
head, I can’t help you.

You don’t need two variables for your view necessarily, but it’s
better practice to have them.

I think this is getting near solving your problem: the extra field in
the DB will make the app so much faster, but you don’t have to worry
about updating it, you can make it update itself.


#8

Tom A. wrote:

Aha.

OK, try this: you could set a new field in your database/model called
“done”. Then what you do is you have an ActiveRecord observer watch
the model, and every time one of its Todo_items gets marked as done,
the observer checks if they’re all done, and if they’re all done, it
sets Todo_list.done to be true. If any of them change status, it
unsets the done status on the list.

Then you CAN just say @active_list = TodoList.find_all(:conditions =>
“done is not null”).

You could either use an observer or a callback on before_save. Have a
look in the agile book or the API documentation - off the top of my
head, I can’t help you.

You don’t need two variables for your view necessarily, but it’s
better practice to have them.

I think this is getting near solving your problem: the extra field in
the DB will make the app so much faster, but you don’t have to worry
about updating it, you can make it update itself.

Tom, thats brilliant. I am just looking again at the Agile book trying
to get my head around the observers. It may take a while. (page 280
onwards).

Tell me Tom, could this help with my other problem I have looming. I
have an overview page that is going to list each of the users projects
and underneath each project there will be a list of the latest 10
updated/created items. These items will be from one of 4 ActiveRecord
models (like messages, todos, tickets) I was trying to work oyt how to
query each project then find the top 10 for each of the has_maany items,
then re iterate over the array of results ordering them in date order.
This would be for each project. Similar to basecamps dashboard (in
function anyway). This is the next hurdle! Would your suggestion here
help as well?


#9

Tom A. wrote:

Also, I should just add: I’m not that brilliant a developer, so other
people may be able to solve this problem better. But there’s LOTS in
those two chapters on ActiveRecord that might help you, so do reread
them.

I must admit i’m not really getting the observer thing yet, i’m trying
it now but can’t seem to get what I want.

STI is out of the question for the items due to many differences and
complexities of the model. I

'm supprised that nobody has explained how the 37sigs guys do the
dashboard thing from an activerecord perspective. It would be most
interesting to see how they have done it with a few examples. Its stuff
like this that needs to be added to Rails Recipes, real world app stuff.


#10

Also, I should just add: I’m not that brilliant a developer, so other
people may be able to solve this problem better. But there’s LOTS in
those two chapters on ActiveRecord that might help you, so do reread
them.