:from parameter in active record find not well designed? - ruby-on-rails

i got this error:
SQLite3::SQLException: no such column: apis.name: SELECT * FROM examples WHERE ("apis"."name" = 'deep')
my code
Api.find :all, :from => params[:table_name], :conditions => {:name => 'deep' }
I need to make a back end rails application which will be used by a silverlight application. one of the requirements is to fetch simple data from the database. i need to be able to query different tables with the same code.(my app has 2000 tables!)
i think it does not make sense for rails to put in "apis" in the WHERE clause. is there any speciic reason for this?

It does that so when joins are performed, the where clauses will line up with the right tables' columns. This is handy most of the time, but in your particular case causes issues.
What you could do is use the other conditions syntax, which will not add rails table names to the attributes, but still sanitize the inputs properly.
Api.find :all, :from => params[:table_name], :conditions => ['name = ?','deep']

Related

Search for users through an associated table via ActiveRecord

I'm building an app in Rails 2.3.18 and I'm trying to find all users who have specific attributes on their profiles. This is found for an individual user by user.profile. User profiles have columns on them like first_name, last_name, birthday, etc.
I'm trying to devise an ActiveRecord query to find all of the users who all have a certain first name, for example.
Something like:
User.find(:where => {:first_name => "Tom"})
How should I format this query?
For Rails 2.3, you want:
User.find(:all, :conditions => 'profiles.first_name = "Tom"', :joins => :profile)
If you want to eager load the profiles, you can use :include instead of :joins like so:
User.find(:all, :conditions => 'profiles.first_name = "Tom"', :include => :profile)
Rails 2.3 rolls pretty much everything into find, so check out the docs for find to see all the available options.
Obviously, if you are interpolating user input into the name condition, be sure to sanitize it.

Polymorphic Relationship Table Queries in Rails — find object by multiple

I have a relationship table in a rails application called edit_privileges, in which the User is the "editor" and a number of other classes are "editable". Let's say that two of those classes are Message and Comment.
My EditPrivilege model uses the following code:
belongs_to :editor, :class_name => "User"
belongs_to :editable, :polymorphic => true
And User, of course
has_many :edit_privileges, :foreign_key => "editor_id"
In order to determine if a user has edit privileges for a certain model, I can't do the normal query:
user.edit_privileges.find_by_editable_id(#message.id)
because if the user has edit privileges to edit a comment with the same id as #message, the query will return true with the wrong edit privilege record from the table.
So, I tried doing these options:
user.edit_privileges.find(:all, :conditions => ["editable_id = ? AND editable_type ?", #message.id, #message.class.to_s])
user.edit_privileges.where(:editable_id => #message.id, :editable_type => #message.class.to_s)
which works great at finding the right record, but returns an array instead of an object (an empty array [] if there is no edit privilege). This is especially problematic if I'm trying to create a method to destroy edit privileges, since you can't pass .destroy on an array.
I figure appending .first to the two above solutions returns the first object and nil if the result of the query is an empty has, but is that really the best way to do it? Are there any problems with doing it this way? (like, instead of using dynamic attribute-based finders like find_by_editabe_id_and_editable_type)
Use find(:first, ...) instead of find(:all, ...) to get one record (note it might return nil while find will raise an RecordNotFound exception). So for your example:
user.edit_privileges.find(:first, :conditions => { :editable_id => #message.id, :editable_type => #message.class.to_s })
BTW, if you're on more edge rails version (3.x), Model.where(...).first is the new syntax:
user.edit_privileges.where(:editable_id => #message.id, :editable_type => #message.class.to_s).first

Cleaning up controllers to speed up application

So in my app I have notifications and different record counts that are used in the overall layout, and are therefore needed on every page.
Currently in my application_controller I have a lot of things like such:
#status_al = Status.find_by_name("Alive")
#status_de = Status.find_by_name("Dead")
#status_sus = Status.find_by_name("Suspended")
#status_hid = Status.find_by_name("Hidden")
#status_arc = Status.find_by_name("Archived")
#balloon_active = Post.where(:user_id => current_user.id, :status_id => #status_al.id )
#balloon_dependent = Post.where(:user_id => current_user.id, :status_id => #status_de.id )
#balloon_upcoming = Post.where(:user_id => current_user.id, :status_id => #status_sus.id )
#balloon_deferred = Post.where(:user_id => current_user.id, :status_id => #status_hid.id )
#balloon_complete = Post.where(:user_id => current_user.id, :status_id => #status_arc.id )
..
Thats really just a small piece, I have at least double this with similar calls. The issue is I need these numbers pretty much on every page, but I feel like I'm htting the DB wayyyy too many times here.
Any ideas for a better implementation?
Scopes
First off, you should move many of these into scopes, which will allow you to use them in far more flexible ways, such as chaining queries using ActiveRecord. See http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/index.html.
Indexes
Second, if you're doing all these queries anyway, make sure you index your database to, for example, find Status quickly by name. A sample migration to accomplish the first index:
add_index :status (or the name of your Status controller), :name
Session
If the data you need here is not critical, i.e. you don't need to rely on it to further calculations or database updates, you could consider storing some of this data in the user's session. If you do so, you can simply read whatever you need from the session in the future instead of hitting your db on every page load.
If this data is critical and/or it must be updated to the second, then avoid this option.
Counter Caching
If you need certain record counts on a regular basis, consider setting up a counter_cache. Basically, in your models, you do the following:
Parent.rb
has_many :children
Child.rb
belongs_to :parent, :counter_cache => true
Ensure your parent table has a field called child_count and Rails will update this field for you on every child's creation/deletion. If you use counter_caching, you will avoid hitting the database to get the counts.
Note: Using counter_caching will result in a slightly longer create and destroy action, but if you are using these counts often, it's usually worth going with counter_cache.
You should only need 1 database query for this, something like:
#posts = Post.where(:user_id => current_user.id).includes(:status)
Then use Enumerable#group_by to collect the posts into the different categories:
posts_by_status = #posts.group_by do {|post| post.status.name }
which will give you a hash:
{'Alive' => [...], 'Dead' => [...]}
etc.

Complex filtering based on a many-to-many relationship

In my application, Annotations are considered "accepted" if either:
They have been explicitly marked "accepted" (i.e., their state == 'accepted')
They were last updated by a user who has the "editor" role
My question is how to find all accepted explanations with a single DB query. Basically I'm looking for the database-driven version of
Annotation.all.select do |a|
a.last_updated_by.roles.map(&:name).include?('editor') or a.state == 'accepted'
end
My first attempt was
Annotation.all(:joins => {:last_updated_by => :roles}, :conditions => ['roles.name = ? or annotations.state = ?', 'editor', 'accepted'])
But this returns a bunch of duplicate records (adding a .uniq makes it work though)
Changing :joins to :include works, but this makes the query way too slow
Are the results of your first attempt just wrong or do they only need an ".uniq"?
Have you tried
:include => {:last_updated_by => [:roles]}
instead of the join?
or making two queries
#ids = Editor.all(:conditions => ["role = 'editor'"], :select => ["id"]).map{|e|e.id}
Annotation.all(:conditions => ["last_updated_by in (?) or state = ?", #ids.join(","), "accepted"]
is that any faster?

Updating a large record set in Rails

I need to update a single field across a large set of records. Normally, I would just run a quick SQL update statement from the console and be done with it, but this is a utility that end users need to be able to run in this app.
So, here's my code:
users = User.find(:all, :select => 'id, flag')
users.each do |u|
u.flag = false
u.save
end
I'm afraid this is just going to take a while as the number of users increases (current sitting at around 35k, adding 2-5k a week). Is there a faster way to do this?
Thanks!
If you really want to update all records, the easiest way is to use #update_all:
User.update_all(:flag => false)
This is the equivalent of:
UPDATE users SET flag = 'f'
(The exact SQL will be different depending on your adapter)
The #update_all method also accepts conditions:
User.update_all({:flag => false}, {:created_on => 3.weeks.ago .. 5.hours.ago})
Also, #update_all can be combined with named scopes:
class User < ActiveRecord::Base
named_scope :inactive, lambda {{:conditions => {:last_login_at => 2.years.ago .. 2.weeks.ago}}
end
User.inactive.update_all(:flag => false)
You could use ActiveRecord's execute method to execute the update SQL. Something like this:
ActiveRecord::Base.connection.execute('UPDATE users SET flag=0')

Resources