ruby-on-rails 在Ruby on Rails中不使用表单将一个布尔属性从false更改为true的链接

8hhllhi2  于 2023-05-02  发布在  Ruby
关注(0)|答案(1)|浏览(98)

我需要能够点击一个按钮或一个链接,将改变一个模型中的布尔属性从假到真,而不使用一个形式。页面上已经有一个表单创建了模型的一个新示例,我不希望两个表单之间的冲突出现在一个页面上。所以,我有一个ItemResponse模型,布尔值为accept-我希望我的链接“接受报价”在部分更改为真,并刷新页面。
我的控制器:

class ItemResponsesController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy, :accept]
  before_action :correct_user, only: :destroy

  def create
    @item_response = current_user.item_responses.build(response_params)
    if @item_response.save
      flash[:success] = "Item Response created!"
      redirect_back(fallback_location: root_url)
    else
      @item = @item_response.item
      render 'items/show', status: :unprocessable_entity
    end
  end

  def destroy
    @item_response.destroy
    flash[:success] = "Item Response deleted"
    redirect_back_or_to( root_url, status: :see_other )
  end

  def accept
    @item_response.update_attribute(:accept, true)
    if @item_response.save
      flash[:success] = "Item Response Accepted!"
      redirect_back(fallback_location: root_url)
    else
      @item = @item_response.item
      render 'items/show', status: unprocessable_entity
    end
  end

  private

    def response_params
      params.require(:item_response).permit(:item_id, :message, :offer, :pickuporship)
    end

    def correct_user
      @item_response = current_user.item_responses.find_by(id: params[:id])
      redirect_to root_url, status: :see_other if @item_response.nil?
    end
end

我的路线:

Rails.application.routes.draw do
  default_url_options :host => "localhost:3000"

  root "static_pages#home"
  get  "/help",    to: "static_pages#help"
  get  "/about",   to: "static_pages#about"
  get  "/contact", to: "static_pages#contact"
  get  "/signup",  to: "users#new"
  get  "/login",   to: "sessions#new"
  post "/login",   to: "sessions#create"
  delete "/logout",to: "sessions#destroy"
  resources :users
  resources :account_activations,   only: [:edit]
  resources :password_resets,       only: [:new, :create, :edit, :update]
  resources :items,                 only: [:create, :destroy, :index, :show]
  resources :looking_for_items,     only: [:create, :destroy, :index, :show]

  resources :item_responses,        only: [:create, :destroy, :accept]
  resources :item_responses do
    member do
      get "accept"
    end
  end

  resources :looking_for_responses, only: [:create, :destroy]
end

我的部分答案是:

<li id="item_response-<%= item_response.id %>">
  <%= link_to gravatar_for(item_response.user, size: 30), item_response.user %>
  <span class="user"><%= link_to item_response.user.name, item_response.user %></span>
  <span class="message"><%= item_response.message %></span>
  <span class="offer"><strong>Offering $<%= item_response.offer %></strong></span>
  <span class="pickuporship">
    <% if item_response.pickuporship %>
      <%= "🛻 Will Pick Up Item" %>
    <% else %>
      <%= "📦 Want Item Shipped" %>
    <% end %>
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(item_response.created_at) %> ago.
    <% if current_user?(item_response.user) %>
      <%= link_to "delete response", item_response, data: { "turbo-method": :delete,
                                            "turbo-confirm": "Are you sure?"} %>
    <% end %>
  </span>
  <% if item_response.accept %>
    <%= "Accepted!" %>
  <% else %>
    <%= "Not Accepted" %>
  <% end %>
    <%= link_to "Accept Offer", accept_item_response_path(item_response) %>
</li>

最后,我的ItemResponse模型:

class ItemResponse < ApplicationRecord
  belongs_to :user
  belongs_to :item

  default_scope -> { order(created_at: :desc) }

  attribute :accept, :boolean, default: false

  validates :user_id, presence: true
  validates :item_id, presence: true
  validates :message, length: { maximum: 2000 }
  validates :offer, presence: true,
                    numericality: { only_integer: true },
                    inclusion: 0..10000
  validates :pickuporship, inclusion: [true, false]
  validate :offer_matches_item_asking_price
  validate :pickuporship_matches_seller_preference
  validates :accept, inclusion: [true, false]

  def offer_matches_item_asking_price
    theitem = Item.find(item_id)
    errors.add(:base, "The seller is not accepting OBOs - please match their asking price") unless ( theitem.obo == true ) || ( theitem.askingprice == offer )
  end

  def pickuporship_matches_seller_preference
    theitem = Item.find(item_id)
    errors.add(:base, "Please select a pickup or ship option that matches the seller's preference") unless ( theitem.pickup == true && pickuporship == true ) || ( theitem.willship == true && pickuporship == false )
  end

end

当我尝试点击上述链接时,我得到一个错误:
undefined methodupdate_attribute' for nil:NilClass`
所以@item_response有问题-它期待一个表单?我真的需要把这个做成一个表格吗?或者我需要使用turbolinks就像我删除/销毁的东西?

hgb9j2n6

hgb9j2n61#

所以@item_response有问题-它期待一个表单?
不。你只是在调用一个你没有定义的示例变量的方法。这是Ruby中的经典初学者脚枪。
我需要能够点击一个按钮或一个链接,将改变一个模型中的布尔属性从假到真,而不使用一个形式。

错了。

实际上,你绝对想要使用表单,因为这是一个non-idempotent action,用来更新服务器上的资源。
使用浏览器发送非幂等请求(POST)是使用表单完成的。链接发送GET请求。GET请求存储在浏览器历史记录中,点击后退按钮不会导致意外后果。
在Rails中,你可以用button_to来处理这个问题,它可以创建一个只有一个按钮的表单,或者用Rails UJS或Turbo处理程序来处理data-methoddata-turbo-method方法的链接。
真实的的区别只是后者使用了一些JavaScript技巧来创建一个没有附加到DOM的表单并提交它。这是一种不太鲁棒和可访问的方法,但可以在表单内部使用(HTML中不允许嵌套表单)。
你实际上应该做的:

resources :item_responses do
  patch :accept, on: :member # the correct HTTP method to use here is debatable
end
class ItemResponsesController < ApplicationController
  # ...
  # This fixes the nil error 
  before_action :set_item_response, only: [:show, :edit, :update, :destroy, :accept]

  # PATCH /item_responses/1/accept
  def accept
    if @item_response.accept
      flash[:success] = "Item Response Accepted!"
      redirect_back(fallback_location: root_url)
    else
      @item = @item_response.item
      render 'items/show', status: unprocessable_entity
    end
  end

  #  ...

  private

  def set_item_response 
    @item_response = ItemResponse.find(params[:id])
  end
end
<% if item_response.accepted? %>
  Accepted!
<% else %>
  Not Accepted
  <%= button_to "Accept Offer", 
    [item_response, :accept], 
    method: :patch
  %>
<% end %>
class AddAcceptedToItemResponses < ActiveRecord::Migration[7.0]
  def change
    # use the correct adjective form and set the default in the schema
    add_column :item_responses, :accepted, :boolean, default: :false
  end
end
class ItemResponse < ApplicationRecord
  # You do not need or want to define attributes if they correspond to a database column
  # You also have so much other garbage in the this model. 
  # - belongs_to assocations are required by default.
  # - you do not need to validate inclusion of booleans. Especially not when they are not set from user input.
  # - default_scope is considered evil
  # ...
  def accept
    update_attribute(accepted: true)
  end
end

相关问题