2012-03-23 11 views
0

Mein Modell:prüft für geladenes SQL-Objekt in Rails

class User 
    has_many :items 
end 

class Item 
    belongs_to :user 

    def self.with_color(color, is_loaded) 
    if is_loaded 
     self.select { |i| i.color == color } 
    else 
     self.where(:color => color) 
    end 
    end 

end 

Es gibt 2 Arten von Code in meinem Controller.

## this line loads 'items' of the user and selects with the red color ## 
@colored_items = current_user.items.with_color('red', false) 

## this code should not load items twice ## 
@user_with_items = User.includes(:items).find(params[:user_id]) 
(...) 
colored_items = @user_with_items.with_color('red', true) 

Wenn ich nur schreiben „wählen Sie“ Version, tut es mit 1 Beispiel arbeiten, und ‚wo‘ Version lädt Elemente wieder, auch wenn sie bereits geladen.

Wie kann ich prüfen, ob das Objekt bereits geladen ist oder nicht?

Antwort

2

Wie wäre es mit Scopes?

aus Beispiel genommen:

class Shirt < ActiveRecord::Base 
    scope :colored, lambda { |color| where(:color => color) } 
end 

Mehr hier zu lesen: http://apidock.com/rails/ActiveRecord/Scoping/Named/ClassMethods/scope

Mit Ihrem Beispiel, sollte dies

class User 
    has_many :items 
end 

class Item 
    belongs_to :user 
    scope :colored, lambda { |color| where(:color => color) } 
end 


## this line loads 'items' of the user and selects with the red color ## 
@colored_items = current_user.items.colored('red') 

## use of 'includes' here to solve (n+1) issue 
@user_with_items = User.includes(:items).find(params[:user_id]) 
colored_items = @user_with_items.colored('red') 

Update (nicht getestet) sein:

class User < ActiveRecord::Base 
    has_many :items 

    def colored_items(color) 
    self.items.to_a.select { |i| i.color == color } 
    end 
end 

user = User.includes(:items).find(1) 
    User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]] 
    Item Load (0.4ms) SELECT "items".* FROM "items" WHERE "items"."user_id" IN (1) 
=> #<User id: 1, name: nil, created_at: "2012-03-25 00:39:52", updated_at: "2012-03-25 00:39:52"> 
user.colored_items "red" 
=> [#<Item id: 1, color: "red", user_id: 1, created_at: "2012-03-25 00:41:00", updated_at: "2012-03-25 00:41:00">] 
user.colored_items "blue" 
=> [#<Item id: 2, color: "blue", user_id: 1, created_at: "2012-03-25 00:41:04", updated_at: "2012-03-25 00:41:04">] 

Also nur 2 SQLs hier.

+0

Umfang ist die gleiche Methode wie self.colored. Ihr erstes Beispiel funktioniert gut, aber das zweite Beispiel führt eine andere Abfrage aus, um nur 'rote' Elemente zu laden. Ich würde @ users_with_items.items.select {| e | tun e.color == 'red'} anstelle von scope, um bereits geladene Objekte zu verwenden. – FancyDancy

+0

Ok, du hast Recht. Scope macht immer ein SQL. Dachte, es würde damit umgehen. Könnte etwas sein, das optimiert werden könnte. Also habe ich eine mögliche Lösung zu meiner Antwort hinzugefügt. – Deradon

+0

Danke -). Genau wie ich brauche. – FancyDancy