#ruby-on-rails #ruby
#ruby-on-rails #ruby
Вопрос:
def follows(follower, followed)
follow = Follows.where("follower = ? AND followed = ?", follower, followed)
if follow
true
else
false
end
end
Вот мой код просмотра:
<% if current_user.id == @user.id%>
<p>This is you!</p>
<% else %>
<% if follows(current_user.id, @user.id)%>
<p>You already follow <%= @user.username %>
<% else %>
<p><%= link_to "Follow!", follow_path(@user.id) %></p>
<% end %>
<% end %>
Я хочу проверить, следует ли пользователь за другим, поэтому написал это. Он принимает два идентификатора пользователя и запрашивает базу данных, и должен возвращать true, когда найдено совпадение, и false в противном случае. Но это всегда возвращает true. Почему это?
Комментарии:
1. Вам наверняка пришлось бы заглянуть в метод Follows.where ?
2. Если функция принимает идентификаторы, то имена параметров должны быть follower_id и followed_id. Резервируйте follower и followed на случай, если у вас есть реальные экземпляры, а не только идентификаторы.
Ответ №1:
Давайте начнем с некоторых проблем стиля и дизайна и закончим фактическим ответом:
-
Модели по соглашению являются единственными. Выполнение в противном случае только приведет к увеличению объема работы. В этом случае я бы предложил
Following
в качестве подходящего имени, например, «у пользователя много подписчиков». -
Внешние ключи должны заканчиваться на
_id
. Выполнение в противном случае только приведет к увеличению объема работы. Такfollower_id
иfollowed_id
. -
Методы, которые предназначены для использования из-за их истинной / ложной природы («методы запроса«), должны заканчиваться на ?, поэтому
follows?
вместоfollows
, -
Ваш оператор if избыточен и может быть безопасно удален, как только условие выполняется правильно. В ruby, в контексте условных выражений, мы больше заботимся о том, оцениваются вещи как true / false, чем о том, являются ли они буквально
true
илиfalse
. Это означает, что все, кромеnil
илиfalse
, будет «правдивым». -
Тот факт, что ваш метод полностью зависит от информации, известной
User
объектам, указывает на то, что было бы лучше повесить его, например, на эти объектыcurrent_user.follows? other_user
. -
Вы дублируете поведение, которое уже было бы предоставлено вам с помощью ассоциаций.
Наконец, принимая во внимание все эти моменты, ответ:
class User < ActiveRecord::Base
has_many :followings, :class_name => 'Following', :foreign_key => 'followed_id'
has_many :followers, :through => 'followings'
def follows?(other)
other.followed_by? self
end
def followed_by?(other)
followers.include? other
end
end
ПРИМЕЧАНИЕ: Использование followed_by?
метода здесь заключается в использовании двойной отправки, которая предотвращает (незначительную) Нарушение закона Деметры, когда один пользователь напрямую знает о состоянии подписчиков другого пользователя. Скорее, первый пользовательский объект задает второму пользовательскому объекту прямой вопрос («Я слежу за тобой?») и основывает результат на ответе. (Вероятно, это также полезный метод сам по себе.)
Комментарии:
1. Большое спасибо, что показали мне правильный способ сделать это, а не просто быстрое исправление!
2. Какой вдумчивый и полный ответ. Хотелось бы, чтобы у меня было больше одного голоса.
Ответ №2:
Причина, по которой это всегда возвращает true, заключается в том, что, даже когда записи не найдены, where() возвращает пустой массив. Пустой массив является «true». В других новостях структура:
if (condition)
true
else
false
end
Может быть заменен на:
condition
Ответ №3:
follow на самом деле является экземпляром ActiveRecord::Relation, а не результирующим набором вашего запроса. Чтобы выяснить, будут ли какие-либо строки возвращены запросом, используйте follow.count. Например.
if follow.count > 0
true
else
false
end
Комментарии:
1. и если вы не делаете что-то еще в блоке, оставьте конечную часть true else false
Ответ №4:
Вы можете использовать present?
. Ваш код должен быть
if follow.present?
true
else
false
end
Комментарии:
1. и если вы не делаете что-то еще в блоке, оставьте конечную часть true else false
Ответ №5:
ответ @rein Heinrichs превосходный. Он предлагает вам лучший способ Rails решить эту проблему. Но я хотел бы объяснить, почему то, что вы написали, не работает, и как вы должны это исправить.
Follows.where(...)
возвращает массив, простой способ убедиться в этом самостоятельно — запустить эту строку в консоли rails (введите rails c
в консоли).
Массив, даже пустой, не является nil
и всегда будет оцениваться как true
.
Итак, чтобы вернуть логическое значение в зависимости от того, найдены ли какие-либо последователи, просто проверьте количество элементов внутри результата where
(используйте size > 0
или present?
)
Таким образом, ваша follows
функция могла быть переписана как:
def follows(follower, followed)
Follows.where("follower = ? AND followed = ?", follower, followed).present?
end
и это на самом деле тоже вполне читаемо. Надеюсь, это поможет.