75 votes

Ruby on Rails will_paginate un tableau

Je me demandais si quelqu'un pouvait m'expliquer comment utiliser will_paginate sur un tableau d'objets ?

Par exemple, sur mon site, j'ai une section d'avis où les utilisateurs peuvent noter les avis. Voici une méthode que j'ai écrite pour rassembler les utilisateurs qui ont évalué l'opinion :

def agree_list
  list = OpinionRating.find_all_by_opinion_id(params[:id])
  @agree_list = []
  list.each do |r|
    user = Profile.find(r.profile_id)
    @agree_list << user
  end
end

Merci.

1 votes

Venu ici à partir d'une recherche Google pour "rails paginate array", je voulais partager avec d'autres visiteurs que kaminari est la solution de pagination des rails que j'ai choisie. Il gère les tableaux de manière élégante.

221voto

keithepley Points 2491

will_paginate 3.0 est conçu pour tirer profit de la nouvelle ActiveRecord::Relation dans Rails 3, il définit donc paginate uniquement sur les relations par défaut. Cela peut toujours fonctionner avec un tableau, mais vous devez demander à rails d'exiger cette partie.

Dans un fichier de votre config/initializers (J'ai utilisé will_paginate_array_fix.rb ), ajoutez ceci

require 'will_paginate/array'

Ensuite, vous pouvez utiliser sur les tableaux

my_array.paginate(:page => x, :per_page => y)

0 votes

Cela m'a aidé mais la pagination ne s'affiche pas correctement dans la vue.

4 votes

Quelqu'un peut-il me dire - comment le rendre en vue ? --- @array = my_array.paginate(:page => x, :per_page => y) <%= will_paginate @array%>

1 votes

@Robbie Guilfoyle my_array.paginate(:page => params[:page], :per_page => 1) Cela a fixé la pagination pour moi :)

9voto

Adam Lassek Points 18918

Vous pourriez utiliser Array#from pour simuler la pagination, mais le vrai problème ici est que vous ne devriez pas utiliser Array du tout.

C'est ce que Associations ActiveRecord sont faites pour. Vous devriez lire ce guide attentivement, il y a beaucoup de choses utiles que vous devrez savoir si vous développez des applications Rails.

Laissez-moi vous montrer une meilleure façon de faire la même chose :

class Profile < ActiveRecord::Base
  has_many :opinion_ratings
  has_many :opinions, :through => :opinion_ratings
end

class Opinion < ActiveRecord::Base
  has_many :opinion_ratings
end

class OpinionRating < ActiveRecord::Base
  belongs_to :opinion
  belongs_to :profile
end

Il est important que le schéma de votre base de données respecte les conventions d'appellation appropriées, sinon tout cela ne fonctionnera pas. Assurez-vous que vous créez vos tables avec Migrations de bases de données au lieu de le faire à la main.

Ces associations créeront des aides sur vos modèles pour faciliter la recherche. Au lieu d'itérer une liste d'OpinionRatings et de collecter les utilisateurs manuellement, vous pouvez faire en sorte que Rails le fasse pour vous grâce à l'utilisation des associations suivantes named_scope o scope selon que vous utilisez Rails 2.3 ou 3.0. Puisque vous n'avez pas précisé, je vais donner les deux exemples. Ajoutez ceci à votre classe OpinionRating :

2.3

named_scope :for, lambda {|id| 
  {
    :joins => :opinion,
    :conditions => {
      :opinion => { :id => id }
    }
  }
}

named_scope :agreed, :conditions => { :agree => true }
named_scope :with_profiles, :includes => :profile

3.0

scope :agreed, where(:agree => true)

def self.for(id)
  joins(:opinion).where(:opinion => { :id => id })
end

Dans les deux cas, vous pouvez appeler for(id) sur le OpinionRatings et lui transmettre un identifiant :

2.3

@ratings = OpinionRating.agreed.for(params[:id]).with_profiles
@profiles = @ratings.collect(&:profile)

3.0

@ratings = OpinionRating.agreed.for(params[:id]).includes(:profile)
@profiles = @ratings.collect(&:profile)

Le résultat de tout cela est que vous pouvez maintenant paginer facilement :

@ratings = @ratings.paginate(:page => params[:page])

Mise à jour pour Rails 4.x : plus ou moins la même chose :

scope :agreed, ->{ where agreed: true }

def self.for(id)
  joins(:opinion).where(opinion: { id: id })
end 

Bien que pour les Rails plus récents, ma préférence aille à kaminari pour la pagination :

@ratings = @ratings.page(params[:page])

0 votes

Les associations sont les suivantes : table Opinion has_many :opinion_ratings, et belongs_to :opinion. Une opinion est liée à la table Profil par une relation many-to-many avec une table de jointure appelée Catégories. Avec cette configuration, savez-vous comment le code que vous avez mentionné ci-dessus serait modifié ?

0 votes

@Brian J'ai mis à jour mon exemple, mais je ne peux pas être certain à moins que vous ne me montriez vos classes modèles.

0 votes

Adam, comment j'arrive au modèle d'évaluation des opinions ? Si je comprends bien, Profile.with_opinion renvoie une liste de tous les profils avec l'id spécifié ? Ce dont j'ai besoin, c'est d'obtenir une liste de profils à partir du modèle OpinionRating, qui appartient à :opinion et Opinion has_many :opinion_ratings. Le modèle Profile est lié au modèle Opinion par la table de jointure Categories.

4voto

iain Points 11602

La pierre précieuse will_paginate paginera à la fois les requêtes ActiveRecord et les tableaux.

list = OpinionRating.where(:opinion_id => params[:id]).includes(:profile).paginate(:page => params[:page])
@agree_list = list.map(&:profile)

1voto

mcclaskc Points 60

Si vous ne voulez pas utiliser le fichier de configuration ou si vous avez des problèmes avec lui, vous pouvez aussi vous assurer de retourner un ActiveRecord::Relation au lieu d'un tableau. Par exemple, changez agree_list en une liste d'identifiants d'utilisateurs, puis faites un IN sur ces identifiants pour retourner une Relation.

def agree_list
  list = OpinionRating.find_all_by_opinion_id(params[:id])
  @agree_id_list = []
  list.each do |r|
    user = Profile.find(r.profile_id)
    @agree_id_list << user.id
  end
  @agree_list = User.where(:id => @agree_id_list) 
end

C'est inefficace du point de vue de la base de données, mais c'est une option pour tous ceux qui ont des problèmes avec le fichier de configuration will_paginate.

0voto

Brian Rosedale Points 1431

J'ai profité des associations de rails, et j'ai imaginé une nouvelle méthode :

def agree_list
  o = Opinion.find(params[:id])
  @agree_list = o.opinion_ratings(:conditions => {:agree => true}, :order => 'created_at DESC').paginate :page => params[:page]
rescue ActiveRecord::RecordNotFound
  redirect_to(profile_opinion_path(session[:user]))
end

Dans ma vue, j'ai regardé le profil comme ça :

<% @agree_list.each do |rating| %>
  <% user = Profile.find(rating.profile_id) %>
<% end %>

S'il y a une meilleure façon de procéder, envoyez un message. J'ai essayé d'utiliser l'aide named_scope dans le modèle OpinionRating, sans succès. Voici un exemple de ce que j'ai essayé, mais qui ne fonctionne pas :

named_scope :with_profile, lambda {|id| { :joins => [:profile], :conditions => ['profile_id = ?', id] } }

Cela revient au même que d'utiliser la méthode de recherche.

Merci pour toute l'aide apportée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X