Почему это всегда возвращает true? Rails

#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:

Давайте начнем с некоторых проблем стиля и дизайна и закончим фактическим ответом:

  1. Модели по соглашению являются единственными. Выполнение в противном случае только приведет к увеличению объема работы. В этом случае я бы предложил Following в качестве подходящего имени, например, «у пользователя много подписчиков».

  2. Внешние ключи должны заканчиваться на _id . Выполнение в противном случае только приведет к увеличению объема работы. Так follower_id и followed_id .

  3. Методы, которые предназначены для использования из-за их истинной / ложной природы («методы запроса«), должны заканчиваться на ?, поэтому follows? вместо follows ,

  4. Ваш оператор if избыточен и может быть безопасно удален, как только условие выполняется правильно. В ruby, в контексте условных выражений, мы больше заботимся о том, оцениваются вещи как true / false, чем о том, являются ли они буквально true или false . Это означает, что все, кроме nil или false , будет «правдивым».

  5. Тот факт, что ваш метод полностью зависит от информации, известной User объектам, указывает на то, что было бы лучше повесить его, например, на эти объекты current_user.follows? other_user .

  6. Вы дублируете поведение, которое уже было бы предоставлено вам с помощью ассоциаций.

Наконец, принимая во внимание все эти моменты, ответ:

 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
  

и это на самом деле тоже вполне читаемо. Надеюсь, это поможет.