I am working in Rails 2.3.x on a learning management system. Here's my code:
-#users.each do |user|
%tr
%td
=link_to h(user.name), :action => :show_user, :id => user.id
%td="#{h user.grade_level}"
-if QuizResult.find_by_user_id(#user_id).present?
="#{(QuizResult.average('score', :conditions => 'user_id = #{#user.id}') * 100).round}%"
-else
%em No quizzes taken
My quiz_results table has columns for user_id and score, among others. I have a record with a user_id (3907) and result (0.1), and two users I'm looking at with no records in the quiz_results table.
The display says "No quizzes taken" for all three, which leads me to believe that this line is false no matter what:
-if QuizResult.find_by_user_id(#user_id).present?
Any ideas how to fix?
Change #user_id to user.id in the if statement and #user.id to user.id. Also change the single quotations to double quotations or using string interpolation won't work.
I guess you need to change #user.id to user.id.
As the answer to second part of your question, I think you can't have a nested evaluated string (the nested #{}). You need to compute the results first, then evaluate and output it in HAML:
-averge = QuizResult.average('score', :conditions => 'user_id = #{#user.id}') * 100).round
="#{average}%"
Related
My aim is to build a relationship between rooms, people and work shift. So my sequel string looks like this :
x = DB[:raum].join_table(:left, DB[:platz], :rid => :id)
.join_table(:left, DB[:patient_behandlungs_link], :platz_id => :id)
.join_table(:left, DB[:patienten], :id => :patienten_id)
.join_table(:left, DB[:behandlungsverfahren], :id => :t2__behandlungsverfahren_id)
.join_table(:left, DB[:dialysezeit], :id => :t2__dialysezeit_id)
.join_table(:left, DB[:nadeln], :id => :t2__dialysenadel_id)
.join_table(:left, DB[:dialysatorzugang], :id => :t2__dialysatorzugang_id)
.where("raumnummer = ?", raumid.to_i)
It's working like this but in the resulting table there is also a field for the shift id. In this state it does not differentiate in which workshift the person is working. if i do a foreach and push the values out I get my empty nil fields with no one inside, which I want to, and I get the people which are in room raumid from all workshifts.
If I make a .filter(:schicht_id => 1) for example, then I loose my nil values. I need them to assign new people to the empty slots, so I tried (:schicht_id => 1).or(:schicht_id => nil) and similar things but I don't get my result, I want
I think i blamed sequel for something that is not related to sequel.
In the image my select box starts showing options from the second value
This behavior made me think, something was wrong with my joins and group by instructions.... i now have to figure out why the select box shows me from values form 2-9 and not from 1-9. In the HTML Site Source code all 9 Options are given.
This is strange for me.
sorry for blaming sequel.
I have a system that has a User, Message, and MessageToken models. A User can create Messages. But when any User reads the Messages of others a MessageToken is created that associates the reader (User) to the Message. MessageTokens are receipts that keep track of the states for the user and that particular message. All of my associations in the Models are set up properly, and everything works fine, except for structuring a very specific query that I cannot get to work properly.
User.rb
has_many :messages
Message.rb
belongs_to :user
has_many :message_tokens
MessageToken.rb
belongs_to :user
belongs_to :message
I am trying to structure a query to return Messages that: Do not belong to the user; AND { The user has a token with the read value set to false OR The user does not have a token at all }
The later part of the statement is what is causing problems. I am able to successfully get results for Messages that are not the user, Messages that the user has a token for with read => false. But I cannot get the expected result when I try to make a query for Messages that have no MessageToken for the user. This query does not error out, it just does not return the expected result. How would you structure such a query?
Below are the results of my successful queries and the expected results.
130 --> # Messages
Message.count
78 --> # Messages that are not mine
Message.where.not(:user_id => #user.id)
19 --> # Messages that are not mine and that I do not have a token for
59 --> # Messages that are not mine, and I have a token for
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(message_tokens: {:user_id => #user.id}).count
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(["message_tokens.user_id = ?", #user.id]).count
33 --> # Messages that are not mine, and I have a token for, and the token is not read
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(message_tokens: {:user_id => #user.id, :read => false}).count
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(["message_tokens.user_id = ? AND message_tokens.read = false", #user.id]).references(:message_tokens).count
The Final Expected Result
52 --> # Messages that are not mine and: I have a token for that is not read OR I do not have a token for
My best attempt at a query to achieve my goal
64 --> # Wrong number returned, expected 52
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(["(message_tokens.user_id = ? AND message_tokens.read = false) OR message_tokens.user_id <> ?", #user.id, #user.id]).references(:message_tokens).count
The problem lies in the query trying to find Messages that are not the users and that the user does not have a token for
63 --> #This does not yield the expected result, it should == 19 (the number of Messages that are not mine and that I do not have a token for)
Message.where.not(:user_id => #user.id).includes(:message_tokens).where.not(message_tokens: {:user_id => #user.id}).count
Message.where.not(:user_id => #user.id).includes(:message_tokens).where(["message_tokens.user_id <> ?", #user.id]).references(:message_tokens).count
How can I solve this?
If you don't mind using 2 queries, a possible solution would be:
messages_not_written_by_user = Message.where.not(:user_id => #user.id)
messages_already_read_by_user = Message.where.not(:user_id => #user.id).includes(:message_tokens).where(message_tokens: {:user_id => #user.id, :read => true})
messages_not_read_by_user_yet = messages_not_written_by_user - messages_already_read_by_user
I would personally find this syntax more readable:
messages_not_written_by_user = Message.where.not(:user => #user).count
messages_already_read_by_user = Message.where.not(:user => #user).includes(:message_tokens).where(message_tokens: {:user => #user, :read => true}).count
One remark to this query:
63 --> #This does not yield the expected result, it should == 19 (the number of Messages that are not mine and that I do not have a token for)
Message.where.not(:user_id => #user.id).includes(:message_tokens).where.not(message_tokens: {:user_id => #user.id}).count
This query searches for all the messages which have a token with an arbitrary other user. (If msg1 has a token with #user, and it also has a token with #another_user, this query will find it.)
Full disclosure - I'm not sure how I'd do this as you have it set up right now. However: are you against installing a gem to help? If you're not, I'd suggest you look into the Squeel gem (https://github.com/activerecord-hackery/squeel).
Squeel makes these kinds of associations a lot easier and allows use to use the plain old | operator. It's built on Arel and shouldn't effect anything you've written in ActiveRecord (at least in my experience). Hope that helps!
Ok, so thanks to the help of R11 Runner I was able to come up with a solution, which required using pure SQL. I could not use the Squeel gem or ActiveRecord as there was no equivalent to SQL's NOT EXISTS operator, which was the crucial component missing.
The reason this works is because unlike the other solutions the NOT EXISTS operator will return all records from the Messages table where there are no records in the MessageTokens table for the given user_id, whereas using where.not would look for the first match instead not ensuring the non existence that was needed.
Message.find_by_sql ["SELECT * FROM messages where messages.user_id <> ?
AND (
(EXISTS (SELECT * FROM message_tokens WHERE message_id = messages.id AND user_id = ? AND read = FALSE))
OR
(NOT EXISTS (SELECT * FROM message_tokens WHERE message_id = messages.id AND user_id = ?))
)",#user.id, #user.id, #user.id]
I'm trying to implement Ryan Bates' sortable table columns code (Railscast #228) but I'd like to be able to sort on an associated column. In particular, I have the following models and associations:
class Project < ActiveRecord::Base
belongs_to :program_manager, :class_name => "User"
class User < ActiveRecord::Base
has_many :program_manager_projects, :class_name => "Project", :foreign_key => "program_manager_id"
The association between the Project model and the User model is mediated by the 'program_manager_id' foreign key, which the user sets in the new/edit views using a collection-select dropdown. Here's part of the annotation at the top of project.rb:
# Table name: projects
# program_manager_id :integer
I want to be able to sort my list of projects in the index view by the program manager's name, i.e., by project.program_manager.name.
Ideally, I'd be able to point :order to this name somehow, perhaps with something like this in the index method of my ProjectsController:
#projects = Project.find(:all, :order => project.program_manager.name)
But that obviously won't work (not to mention Ryan's routine implements this with a specific reference to table names from the model to be sorted.)
I've come across some intimidating approaches that use named_scope, such as:
named_scope :most_active, :select => "questions.*", :joins => "left join comments as comments_for_count on comments_for_count.question.id = questions.id", :group => "questions.id", :order => "count(questions.id) desc"
But given my lack of MySQL expertise, this is fairly impenetrable to me.
Can anyone help me either generalize the named_scope example above for my specific case, or point me to a more straightforward strategy?
Thanks very much,
Dean
Let's dissect that named scope you referenced above. Imagine a model Question which has many Comments.
named_scope :most_active, :select => "questions.*", :joins => "left join comments as comments_for_count on comments_for_count.question.id = questions.id", :group => "questions.id", :order => "count(questions.id) desc"
:most_active
the name of your scope. You would reference thusly: Question.find(:all).most_active
:select => "questions.*"
by default scopes selects all columns from your table anyway, so this limits the results to only the questions table, and not the comments table. This is optional.
:joins => "left join comments as comments_for_count on comments_for_count.question.id = questions.id"
this is saying for every question, I also want to get all comments associated with them. The comments table has a column 'question_id' which is what we'll be using to match them up to the appropriate question record. This is important. It allows us access to fields that are not on our model!
:group => "questions.id"
This is required for the count() function in the order clause to tell us that we want the count of comments based on question. We don't need the count function in our order clause, so we also don't need this group statement
:order => "count(questions.id) desc"
Return the results in order of number of comments, highest to lowest.
So for our example, discarding what we don't need, and applying to your needs, we end up with:
:named_scope :by_program_manager_name, :joins => "left join users on projects.program_manager_id = users.id", :order => "users.name"
This named_scope would be called thusly:
Project.find(:all).by_program_manager_name
Note this is basically equivalent to:
Project.find(:all, :joins => "left join users on projects.program_manager_id = users.id", :order => "users.name")
But, as cam referenced above, you should really know the underlying SQL. Your abilities will be severely hampered without this understanding
I currently have a table that is listed as follows:
projects = Project.find(:all, :conditions => [conditions + "AND (name LIKE ? OR description LIKE ?)", "%#{params[:query]}%", "%#{params[:query]}%"])
where
conditions = Project.in_used_projects(:alias => "projects")
However I need to include a 3rd variable which is not from the Project table but from a Tags table. The column I need is Tag - > Names. Is there anyway I can bind variables from another table in Ruby? The Project.find(all) automatically passes the SELECT * FROM Project into MYSQL. Someone has suggested using a join function, but I'm not sure how this would work. Any ideas?
EDIT 1
I have tried the suggested answer of using
projects = Project.find(:all, :joins => "tags", :conditions => [conditions + "AND (projects.name LIKE ? OR description LIKE ? OR tags.name LIKE ?", ["%#{params[:query]}%" * 3]].flatten)
but now I am getting another error
Mysql::Error: Unknown table 'projects': SELECTprojects.* FROMprojectstags WHERE ((projects.status = 2)AND (projects.name LIKE '%%' OR projects.description LIKE '%%' OR tags.name LIKE '%%')
Very strange considering the projects table exists. Why isn't Ruby recognizing it now that i've included another table?
Try this out for size:
projects = Project.find(:all, :joins => "tags", :conditions => [conditions + "AND (projects.name LIKE ? OR description LIKE ? OR tags.name LIKE ?", ["%#{params[:query]}%" * 3]].flatten)
The :joins option tells Active Record to perform an SQL join onto the tags table, which will allow you to perform queries on the tags column. Also take note in this example how I've added the projects. prefix to your original name column. This is so your database doesn't get confused what name column you want; the one from the projects or the tags table.
Do you have any relationship between projects and tags? If the reason for your query is that there is, it should be reflected in the model (for ex with a HABTM), which would also enable you to easily filter based on that relationship without the need to have complex SQL queries crafted.
Turns out that it was just a simple syntax error
projects = Project.find(:all, :joins=>:tags, :conditions => [conditions + "AN rojects.name LIKE ? OR projects.description LIKE ? OR tags.name LIKE ?)", "%# ams[:query]}%", "%#{params[:query]}%", "%#{params[:query]}%"])
is the correct syntax. The :joins needs to be followed by :tags, not by "tags"
Thanks again for everyone's help
When querying the database with:
#robots = Robot.all(:condition => [:a => 'b'], :limit => 50, :offset => 0)
What is the best way to get the total number of rows without the :limit?
In raw MySQL you could do something like this:
SELECT SQL_CALC_FOUND_ROWS * FROM robots WHERE a=b LIMIT 0, 50
SELECT FOUND_ROWS();
Is there an active record way of doing this?
This works for me:
ps = Post.all(:limit => 10, :select => "SQL_CALC_FOUND_ROWS *")
Post.connection.execute("select found_rows()").fetch_hash
=> {"found_rows()"=>"2447"}
This will probably not work for joins or anything complex, but it works for the simple case.
Robot.count actually is the solution you want.
Reading one of the comments above, it looks like you may have a misunderstanding of how .count works. It returns a count of all the rows in the table only if there's no parameters.
but if you pass in the same conditions that you pass to all/find eg:
Robot.count(:conditions => {:a => 'b'})
.count() will return the number of rows that match the given conditions.
Just to be obvious - you can even save the condition-hash as a variable to pass into both - to reduce duplication, so:
conds = {:a => 'b'}
#robots = Robot.all(:conditions => conds, :limit => 50)
#num_robots = Robot.count(:conditions => conds)
That being said - you can't do an after-the-fact count on the result-set (like in your example). ie you can't just run your query then ask it how many rows would have been found. You do actually have to call .count on purpose.
search = Robot.all(:condition => ["a=b"], :offset => 0)
#robots = search[0..49]
#count = search.count
That should get what you want, gets all the Robots for counting and then sets #robots to the first 50. Might be a bit expensive on the resource front if the Robots table is huge.
You can of course do:
#count=Robot.all(:condition => ["a=b"], :offset => 0).count
#robots=Robot.all(:condition => ["a=b"], :limit => 50, :offset => 0)
but that will hit the database twice on each request (although rails does have query caching).
Both solutions only use active record so are database independent.
What do you need the total returned by the query for? if its pagination look into Will_paginate (Railscast) which can be extended with AJAX etc...
Try find_by_sql may that help.
Is #robots.size what you're looking for? Or Robot.count?
Otherwise, please clarify.
I think hakunin is right.
You can get no of row return by query by just chekcing the size of resulting array of query.
#robots = Robot.find_by_sql("Your sql")
or
#robots = Robot.find(:all , :conditions=>["your condiitons"] )
#robots.size or #robots.count