2010-04-20 3 views
53

Я чувствую, что это должно быть очень просто, но мой мозг замыкается на нем. Если у меня есть объект, представляющий текущего пользователя, и вы хотите запросить всех пользователей, кроме текущего пользователя, как я могу это сделать, учитывая, что текущий пользователь может иногда быть nil?Rails ActiveRecord: Найти всех пользователей, кроме текущего пользователя

Это то, что я делаю прямо сейчас:

def index 
    @users = User.all 
    @users.delete current_user 
end 

Что мне не нравится то, что я делаю пост-обработку результата запроса. Кроме того, чувствуя себя немного не так, я не думаю, что это сработает хорошо, если я конвертирую запрос на запуск с will_paginate. Любые предложения по тому, как это сделать с запросом? Благодарю.

+1

На мой взгляд, когда я запрос на то, что я должен позволить часть моего приложения, оптимизированная для обработки запроса: база данных. Поэтому пост-обработка результатов запроса для дальнейшего его фильтрации, для меня, помещает логику в неправильное место. – SingleShot

ответ

119

можно сделать следующее в Rails 4:

User.where.not(id: id) 

Вы можете обернуть его в хорошем объеме.

scope :all_except, ->(user) { where.not(id: user) } 
@users = User.all_except(current_user) 

Или использовать метод класса, если вы предпочитаете:

def self.all_except(user) 
    where.not(id: user) 
end 

Оба метода возвращают объект AR отношение. Это означает, что вы можете цепной метод вызывает:

@users = User.all_except(current_user).paginate 

Вы можете исключить любое количество пользователей, поскольку where() также принимает массив.

@users = User.all_except([1,2,3]) 

Например:

@users = User.all_except(User.unverified) 

И даже через других ассоциаций:

class Post < ActiveRecord::Base 
    has_many :comments 
    has_many :commenters, -> { uniq }, through: :comments 
end 

@commenters = @post.commenters.all_except(@post.author) 

Смотрите в API Docswhere.not().

+1

Итак, подсчитывает ли счет upvote принятый ответ? – wurde

+0

@wurde no: D - но он будет там, чтобы люди увидели, от каких ответов пользователи получают наибольшую отдачу. – Mohamad

+0

Должен размещать баннер на принятых ответах, с которыми сообщество не соглашается. Подобно тому, как правительству нужны баннеры на коробках с сигаретами. – wurde

32
@users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id])) 
+0

Спасибо за предложение named_scope. Он немного очистил контроллер и сделал его менее уродливым, когда «обновить» до will_paginate. Еще раз спасибо. – SingleShot

-5

Что вы делаете, это удаление current_user из массива @users. Это не будет работать, поскольку для массивов не существует метода удаления. То, что вы, вероятно, хотите сделать это

def index 
    @users = User.all 
    @users - [current_user] 
end 

Это возвращает копию массива @users, но со снятым объектом current_user (он содержится в массиве в первую очередь.

Примечание : Это может не сработать, если вычитание массива основано на точном совпадении объектов, а не с содержимым, но оно работает со строками, когда я его пробовал. Не забудьте заключить current_user в [], чтобы вставить его в массив.

+0

Спасибо за ваш ответ. Представленный мной код кода действительно работает, потому что на Array действительно существует метод удаления. К сожалению, ваши предложения не совсем достигают того, что я ищу. Еще раз спасибо. – SingleShot

+3

>> Array.new.methods.grep/delete/ => ["delete_at", "delete_if", "delete"] – user253455

+2

Этот ответ также неверен, поскольку частичное представление индекса использует переменную экземпляра @users, которая не изменяется по операции '-'. Не имеет значения, что возвращает метод 'index'. (Вам нужно было использовать оператор = =, чтобы изменить @users на месте) – ndbroadbent

7

более короткий вариант:

User.all :conditions => (current_user ? ["id != ?", current_user.id] : []) 
+0

Я взял ответ от пользователя jdl, его комментарий о названных областях и вашу доработку, и теперь я счастлив. Контроллер довольно чистый, хотя named_scoped немного уродлив. После добавления will_paginate вот мой запрос: User.all_except (current_user) .paginate (: page => params [: page]) – SingleShot

+0

@SingleShot Я отредактировал ответ. –

+0

Выглядит хорошо. –

16

Вы также можете создать named_scope, например. в модели:

named_scope :without_user, lambda{|user| user ? {:conditions => ["id != ?", user.id]} : {} } 

и в контроллере:

def index 
    @users = User.without_user(current_user).paginate 
end 

Эта область будет возвращать все пользователи при вызове с нуля и всех пользователей, кроме приведены в парам в другом случае. Преимущество этого решения заключается в том, что вы можете связать этот вызов с другими именованными областями или с помощью метода_пагинации paginate.

+0

Это лучший ответ. Храните данные в области действия как можно дольше. Также, как большинство запросов будут работать в Rails 3 с Arel. – Gdeglin

+0

Использовать методы класса вместо лямбда области видимости с параметрами – Hauleth

6

Одно примечание на ответ GhandaL - по крайней мере, в Rails 3, это стоит изменения в

scope :without_user, lambda{|user| user ? {:conditions => ["users.id != ?", user.id]} : {} } 

(первичного изменения здесь от «users.id = 'ид = ...!'!. .. '; также область вместо named_scope для Rails 3)

Оригинальная версия отлично работает при простом просмотре таблицы Users. При применении области действия к ассоциации (например, team.members.without_user (current_user) ....) это изменение потребовалось, чтобы уточнить, какую таблицу мы используем для сравнения id. Я видел ошибку SQL (используя SQLite) без нее.

Извинения за отдельный ответ ... У меня еще нет репутации, чтобы прокомментировать непосредственно ответ GhandaL.

2

Очень простое решение я использовал

@users = User.all.where("id != ?", current_user.id) 
0

Другой простой способ, которым Вы могли бы сделать это:

@users = User.all.where("id NOT IN(?)", current_user.id) 
0

User.all.where ("? ID NOT IN()", current_user.id) будет через исключение неопределенный метод, где для #<Array:0x0000000aef08f8>

User.where("id NOT IN (?)", current_user.id) 
0

массив будет больше помощи FUL

arrayID [0] = 1

arrayID [1] = 3

User.where.not (ID: arrayID)

Смежные вопросы