【问题标题】:Is this the "proper" way to update an attribute in Rails app?这是在 Rails 应用程序中更新属性的“正确”方式吗?
【发布时间】:2012-01-23 23:41:14
【问题描述】:

感谢收看。我正在学习 Rails 并正在构建一个 Web 应用程序。我试图成为“RESTful”并以这些术语思考。因此,我将我的数据视为一种资源。假设我有一个名为 microposts 的表格和一个“投票”列,其中计算有多少人赞成或反对。我是这样工作的:

在视图中:

<td>
    <%= form_for feed_item do |f| %>
        <div><%= hidden_field_tag 'todo', 'upvote' %></div>
        <div class="action"><%= f.submit "Up" %></div>
    <% end %>
</td>
<td>
    <%= form_for feed_item do |f| %>
      <div><%= hidden_field_tag 'todo', 'downvote' %></div>
      <div class="action"><%= f.submit "Down" %></div>
    <% end %>
<td>

当用户点击按钮时,它会调用控制器中的“更新”方法。然后,在控制器中,我有这个更新:

  def update
    @todo = params[:todo]
    if @todo == "upvote"
      Micropost.find(params[:id]).increment!(:votes)
      flash[:success] = "Increase micropost vote"
      redirect_to root_path
    elsif @todo == "downvote"
      Micropost.find(params[:id]).decrement!(:votes)
      flash[:success] = "Decrease micropost vote"
      redirect_to root_path
    else
      redirect_to root_path
    end
  end

我通过一个隐藏字段来了解按下的按钮是“赞成”还是“反对”。所以,如果我想改变任何其他微博属性,我需要越来越多的 if 语句。我不认为我这样做是正确的。更新数据库中的属性或记录的 Rails 方法是什么?

非常感谢!

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 activerecord


    【解决方案1】:

    我会创建额外的操作upvote/downvote(或只是vote)并将它们添加为member 资源。 REST 并不仅限于 CRUD,您始终可以添加其他操作。例如,在 Rails 2 中是:

    map.resources :my_resource, :member =&gt; {:upvote =&gt; :put}

    然后您可以使用upvote_my_resource_path 并将upvote 操作添加到您的控制器,这样您就不会弄乱update

    【讨论】:

    • 虽然是一种有效的方法,但它并不是问题所要求的纯 RESTful。并不是说我认为 REST 总是答案。
    • 就我而言,我使用的是 Rails 3.0,它看起来像这样吗?资源 :microposts 会员做 :upvote, :downvote end end?谢谢
    【解决方案2】:

    更 RESTful 的改造是将您的每个操作定义为单独的路由,如果必须,则 GET,理想情况下为 POST,以接收这些类型的调用中的每一个:

    before_filter :load_micropost
    
    def load_micropost
      @micropost = Micropost.find(params[:id])
    end
    
    def upvote
      @micropost.increment!(:votes)
      flash[:success] = "Increase micropost vote"
      redirect_to root_path
    end
    
    def downvote
      @micropost.decrement!(:votes)
      flash[:success] = "Decrease micropost vote"
      redirect_to root_path
    end
    

    不要忘记创建像load_micropost 这样的方法来处理模型的例行加载。对您的方法散布多个不同的调用是没有意义的,这些调用在将来的某个时候可能需要调整。

    你会像这样路由这些:

    map.resources :microposts,
      :member => {
        :upvote => :post,
        :downvote => :post
      }
    

    您可能要考虑的另一件事是添加一个表格,记录谁投票支持什么,以便您可以在需要时进行审核和调整。这样可以轻松解除任何欺诈活动,而无需批量重置选票。

    如果您要求,您也可以创建简单的 POST 链接:

    <%= link_to('Upvote', upvote_micropost_path(feed_item), :method => :post) %>
    

    这将负责渲染所需的任何表单元素。

    【讨论】:

    • GET 具有破坏性作用?想都别想! REST 保留 PUT 用于更新,在这种情况下,IMO 比 POST 更好。
    • PUT 用于更新,POST 通常会创建新记录。通常投票创建一次,很少调整,这就是为什么POST 在这里可能是合适的。你说得对,GET 是因为链接预加载器等问题。这就是为什么我说“如果你必须”。
    • 谢谢塔德曼!我在 Rails 3 上,所以我使用 :put 并且您的解决方案有效
    【解决方案3】:

    我会在单独的模型Vote(micropost_id, user_id, positive, created_at) 中保存投票,还将字段添加到微帖子votes 或类似的东西,其中将包含(count_up_votes - count_down_votes)

    之后我会在微博下创建新资源votes,这样你就有了micropost_votes_path这样的路由。

    如果您选择该设计,您的用户将能够取消他们的投票,并且您将能够防止每个用户多次投票。

    【讨论】:

      【解决方案4】:

      首先,为你的模型添加一个虚拟属性:

      Class Micropost
        def change_vote(value)
          if value == 'upvote'
            votes += 1
          elseif value == 'downvote'
            votes -= 1
          end
        end
      end
      

      然后在你的表单中,一定要让它使用表单助手:(假设 feed_item 是一个微博)

      <%= form_for feed_item do |f| %>
          <div><%= f.hidden_field 'change_vote', 'upvote' %></div>
          <div class="action"><%= f.submit "Up" %></div>
      <% end %>
      

      这会将资源以适当的形式返回给 REST。控制器需要做的就是:

      def update
        @mp = Micropost.find(params[:id])
        @mp.update_attributes(params[:micropost])
        if params[:micropost][:change_vote] == 'upvote' 
          flash[:success] = "Increase micropost vote"
        else
          flash[:success] = "Decrease micropost vote"
        end
        redirect_to root_url
      end
      

      当然,这还没有考虑验证等,但这是 RESTful 的方式

      【讨论】:

      • 我想这与原始问题中的内容完全相同,至少从 RESTful 的角度来看是这样。真正的 RESTful 方式可能是创建带有投票计数的微调器,使用 edit 操作显示它,并将其作为更新请求连同其他属性一起发送回。另一个问题是谁需要这样的东西。
      • 不太一样,从某种意义上说,原始帖子并不平静,因为它没有在请求中传递实际资源。除此之外,它当然会被重构以将逻辑放入模型中,因为它应该是。我的方式,无论是更新其他属性还是投票都可以使用update调用;这都是正在更新的资源。
      • 这里有太多不必要的重复,以至于混淆了实际正在做的事情。使用attr_accessor 作为触发保存行为的方式既懒惰又危险。
      • 不是attr_accessor,是虚拟属性。与加密密码相同的技术。见railscasts.com/episodes/16-virtual-attributes至于重复,什么重复?这个解决方案减少了代码,我没有看到任何重复。 (闪存消息有点奇怪,但只是为了保持相同的功能)。此外,此代码可以与 Micropost 对象的定期更新共存……进一步减少重复。设置新的路线、方法、发现...那是不必要的重复。
      • 我想我必须澄清一下,我不需要它是“真正”宁静的,但我希望它是做事的“轨道方式”。
      猜你喜欢
      • 2012-09-25
      • 2023-01-03
      • 1970-01-01
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      • 2017-04-01
      • 1970-01-01
      • 2013-02-18
      相关资源
      最近更新 更多