rails joins conditions as hash - mysql

Course.find(:all, :group =>:id, :order => 'updated_at DESC', :joins=> :students :conditions => { :students => { :first_name=>"John", :status => 1}})
looking this query, passing the conditions as a hash, there is a way to:
construct a where :first_name not null?
construct a where :first_name != "John"?

Natively, there is not a way of which I am aware. There is ar-extensions which extends the finders with many things, including negating.
:conditions => { :students => { :first_name_not => "John"}}
:conditions => { :students => { :first_name_not => nil}}
Fair warning, last update I see for it is a year ago and support is limited to postgre, mysql and sqlite. This is the only active project I am aware of that extends activerecord in this way. Thoughtbot had squirrel, which you might be able to find some active forks for.

Related

(Rails) Does scoping apply when calling model.association.find?

For example, I have:
class School < ActiveRecord::Base
has_many :students
end
one_school = School.first
Is there a speed difference between using:
Student.find :all, :conditions => { :first_name => "John", :school_id => one_school.id }
and
one_school.students.find :all, :conditions => { :first_name => "John" }
I was wondering if calling "one_school.students.find" will iterate through ALL the student records, or will they iterate through the student records belonging to one_school only?
This is more of a question on performance. I need to know if the latter query really is faster in rails.
That's basically the same (Rails will use a join in both cases), you can see the query and it's performance in your server console and compare. To enhance performance make sure you index school_id on your students table.

Ordering JSON rendered by Rails controller

I have a Rails 3.1 controller that renders a user's contacts, including associated email objects and message objects. If I am only rendering the contacts, I can do the following:
#contacts = #current_user.contacts.order('last_name asc', :include => [:emails, :messages])
render json: #contacts, :include => [:emails, :messages]
As you can see, I want to sort the contacts by last name rather than the default id. I am now needing to render the user object with other associated objects as well. So I have tried the following, but of course the contacts are not in the appropriate order:
render :status => 200, :json => {
:user => #current_user.as_json(
:include => {
:foos => {
:except => :user_id
},
:contacts => {
:except => :user_id,
:include => [:emails,:messages]
},
:bars => {
:except => :user_id
}
}
)
}
I didn't see any help in the as_json documentation, and I haven't been able to find the right syntax by trial and error.
In this case I would order the contacts in Ruby / SQL and just build your own JSON to render instead of using as_json and its various :include / :except methods.
Build a hash of your data and then send it along to render.
There are all sorts of libraries that can make building JSON easier. JBuilder is one such library. Look at the bottom of the JBuilder page for links to other similar libraries.

Rails activerecord order by field in related table

I have a typical forum style app. There is a Topics model which has_many Posts.
What I want to do using Rails 2.3.x is query the topics table and sort by the most recent post in that topic.
#topics = Topic.paginate :page => params[:page], :per_page => 25,
:include => :posts, :order => 'HELP'
I'm sure this is a simple one but no joy with Google. Thanks.
Sorting on a joined column is probably a bad idea and will take an enormous amount of time to run in many situations. What would be better is to twiddle a special date field on the Topic model when a new Post is created:
class Post < ActiveRecord::Base
after_create :update_topic_activity_at
protected
def update_topic_activity_at
Topic.update_all({ :activity_at => Time.now }, { :id => self.topic_id})
end
end
Then you can easily sort on the activity_at column as required.
When adding this column you can always populate the initial activity_at with the highest posting time if you have existing data to migrate.

How do I set the default format, like JSON, for a customized Devise controller in Rails 3?

My Rails 3 app has three customized controllers for my Devise + OmniAuth integration. I needed to override the standard methods, like 'new', for user registrations and sessions. I specifically needed the controller methods to handle redirects and responses that are compatible with JSON formatting.
In my routes.rb file I have the following:
devise_for :users, :controllers => {
:omniauth_callbacks => "users/omniauth_callbacks",
:registrations => "users/registrations",
:sessions => "users/sessions"
}
That works as expected. My routes now show the custom controller routes like:
new_user_session GET /users/sign_in(.:format) {
:action =>"new",
:controller =>"users/sessions"
}
new_user_registration GET /users/sign_up(.:format) {
:action=>"new",
:controller=>"users/registrations"
}
To set the default format for a resource I would do something like this:
resources :users, :defaults => {
:format => 'json'
}
So, I tried this:
namespace "users" do
resources :registrations, :defaults => {
:format => 'json' }
resources :sessions, :defaults => {
:format => 'json' }
end
Which did not work as expected. I ended up with these routes:
new_users_registration GET /users/registrations/new(.:format) {
:format=>"json",
:action=>"new",
:controller=>"users/registrations"
}
new_users_session GET /users/sessions/new(.:format) {
:format=>"json",
:action=>"new",
:controller=>"users/sessions"
}
In order for this to work with my custom overrides in Devise, I need to format 'new_user_registration' not 'new_users_registration'.
I checked the 'devise_for' method and it does not have a :defaults option. I can use the 'devise_scope' method to set the individual routes, but that seems far less concise that the :defaults idiom.
Does anyone know of any routing magic that I can use to make this happen?
I found an answer that isn't necessarily satisfying, but it works. I tried this in routes.rb:
devise_scope :user do
get "sign_up", :to => "users/registrations#new",
:defaults => { :format => 'json' }
end
And I tried this in my custom controllers:
redirect_to new_user_registration_url, :format => 'json'
Neither worked. I am guessing both of those were incorrect in implementation. I finally used this in my custom controllers:
redirect_to :controller => 'users/registrations',
:action => 'new',
:format => 'json'
That replaced everywhere I originally had:
redirect_to new_user_registration_url
It's more verbose than I like and not very DRY, but it works.

ActiveRecord - pre fetch single records from association

Consider the following models..
class Product < ActiveRecord::Base
has_many :pricings
end
class Pricing < ActiveRecord::Base
belongs_to :server
end
Pricing is a historical tracking table for prices of products, so there may potentially be hundreds of price points captured over time. What I want to add is a way to get only the current pricing for the product.
While i can add a has_one relation to the model like the following:
has_one :current_pricing, :class_name => "Pricing", :foreign_key => "product_id",
:order => 'created_at DESC'
This will fetch me all the Pricings for the current product before returning me only the first record.
I am looking for a way to accomplish this in a more efficient manner and was hoping that someone would have input or previous experience doing something similar.
You could use a named scope. Put this at the top of the Pricing model:
class Pricing < ActiveRecord::Base
named_scope :current, lambda { |product| { :conditions => { :product_id => product.id }, :order => 'created_at DESC', :limit => 1 } }
end
Then to get the current pricing of a product, you'd call "Pricing.current(product).first".
In addition to the named scope on pricing, you could add an accessor method to the Product model like so:
class Product < ActiveRecord::Base
def current_pricing
Pricing.current(self).first
end
end
You're looking for a named_scope.Define this in your Pricing model.Supply it with 1 parameter, the product_id.
named_scope :latest_price, lambda { |*args| {:conditions => {:product_id => args.first}, :order => "created_at DESC", :limit => 1} }
Excellent screencast on this by the way.
Add a :limit to the query?