2011-07-29 3 views
3

Je me demandais si les gens pouvaient partager leur approche préférée pour filtrer le contenu d'une relation has_many par une autre clé étrangère. Par exemple, supposons que vous ayez trois entités: Départements, Employés, Projets. Un département a beaucoup d'employés, et un projet a beaucoup d'employés. J'ai un projet, et je veux obtenir tous ses employés d'un département donné. Mon code ressemble à ceci:Rails: Filtre relation has_many par une autre clé étrangère

# department.rb 

has_many :employees 

# employee.rb 

belongs_to :department 
belongs_to :projects 

# project.rb 

has_many :employees 

Maintenant, je peux penser à quatre approches à mon problème:

APPROCHE 1: méthodes d'interrogation de classe:

# anywhere.rb 

Employee.where(:project_id => project, :department_id => department) 

APPROCHE 2: Méthode d'assistance

# project.rb 

def employees_from_department(department) 
    employees.select { |emp| emp.department == department } 
end 

APPROCHE 3: méthode d'assistance sur la relation

# project.rb 

has_many :employees do 
    def from_department(department) 
    where(:department_id => department) 
    # Could also be all.select { |emp| emp.department == deparment } 
    end 
end 

APPROCHE 4: Scopes

# employee.rb 

scope :from_department, lambda { |department| 
    where(:department_id => department) 
} 

# anywhere.rb 

project.employees.from_department(department) 

Je choisis presque toujours l'approche # 4, car il est le plus réutilisable. Je peux appliquer cette portée à n'importe quelle requête qui renvoie Employees. Je peux combiner avec d'autres champs d'application, définir une commande, etc. Plus à l'aide des étendues signifie toute ma lecture seule, le code de style de requête est nommé assez cohérente et organisée en haut, donc j'avoir moins de méthodes. Les portées sont l'une de mes fonctionnalités Rails préférées. Mais je me trouve en train de les écrire/tout le temps /, de sorte que j'ai presque une portée paramétrée pour correspondre à chaque: belongs_to. Est-ce la bonne approche? En outre, il semble que je générer une tonne de requêtes de base de données, alors je me demande si je suis battu des Rails de mise en cache aurait pu faire pour moi, parce que mon champ est contraint Rails pour aller à la base de données à chaque fois.

Ceci est en partie une question de performance, ce qui signifie qu'il n'y a pas une taille unique toute réponse, et vous avez besoin de tester le code dans la production de trouver la bonne façon. Mais avant que votre code ne soit en production, quelle approche avez-vous tendance à choisir? Ou s'agit-il d'autre chose?

+0

# 1, # 3 devrait générer le même SQL (sauf si vous utilisez 'all.select' dans # 3). Quant à # 4, où est le 'projet'? N'utilisez pas # 2, il récupérera des données excessives, 'employees.find (: all,: conditions => ...)' ou '.find_by _...' ou probablement 'where' sont beaucoup mieux dans ce cas. –

+0

@Victor: J'ai modifié le code pour # 4 pour montrer comment la portée est utilisée. –

Répondre

2

Je préfère personnellement les champs d'application (4) approche ou les méthodes de classe.

Je crois en cas normal, votre approche 4 et approche 1 devrait générer la même instruction SQL si vous utilisez la portée enchaînée, comme:

project.employees.from_department(department_id) 

Vous pouvez essayer d'ajouter un appel .to_sql à elle dans la console , pour voir le sql réel généré. Pour l'analyse des performances (sql), vous pouvez utiliser des outils tels que query-reviewer ou rack-bug, pratiques et utiles.

+0

Je suis d'accord que # 1 et # 4 devrait générer le même SQL. Merci pour les liens vers les outils d'analyse SQL! –

1
project.employees.find_by_department_id(department) 

(essentiellement identique à # 3)

Questions connexes