对RoR来说真的很新,遇到了一个我无法找到答案的问题。
我有两个表,user
和books
,我正在尝试使用联接表来匹配用户和图书,并在联接表中添加评分和审核列。
问题是除了book_id
查看
<%= form_for(current_user.user_book_collections.build) do |f| %>
<div>
<%= hidden_field_tag :book_id, current_book.id %>
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :review %> (optional):
<%= f.text_area :review, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
</div>
<p></p>
<%= f.submit "Add to your collection", class:"btn btn-primary" %>
<% end %>
current_user
和current_book
是分别分配用户和图书的方法。
控制器
def create
@user_book_collection = current_user.user_book_collections.build(user_book_collection_params)
if @user_book_collection.save
flash[:success] = "Added to your collection"
redirect_to mybooks_path
else
flash[:danger] = "Add was unsuccessful"
redirect_to bookcollection_path
end
end
private
def user_book_collection_params
params.require(:user_book_collection).permit( :book_id, :user_id, :review, :rating )
end
end
这是控制台中显示的内容:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"85gmzUO7ldQrevh/qnKwYO9mkd9lX77sG3xxJQV8Y46xZrkl5ifk665abPr79nOT91rO3oLcMSDgYL7BtR+/XQ==", "book_id"=>"6", "user_book_collection"=>{"rating"=>"s", "review"=>"asd"}, "commit"=>"Add to your collection"}
但是然后在控制台中检查记录我可以看到book_id没有通过,即使它已在参数中分配:
=> #<ActiveRecord::Associations::CollectionProxy [#<UserBookCollection id: 10, user_id: 7, book_id: nil, review: "asd", rating: "s", created_at: "2016-02-07 06:36:33", updated_at: "2016-02-07 06:36:33">
任何帮助都将不胜感激,谢谢。
答案 0 :(得分:0)
这是因为您的表单是以book_id
传递params[:book_id]
的方式构建的,而不是params[:user_book_collection][:book_id]
中传递的user_book_collection_params
使用:
f.hidden_field :book_id, value: current_book.id
答案 1 :(得分:0)
<%= f.hidden_field :book_id, current_book.id %>
使用form_for
时,需要将输入方法绑定到表单对象(如上所述)。
答案 2 :(得分:0)
除了Rich Peck和Vasfeds的优秀答案之外,我建议你从REST的角度考虑你在做什么。
您希望用户能够将评论添加到图书中。请注意,这里的UserBookCollections只是一个丑陋的管道 - 不是真正用REST公开的资源。并且将其称为UserBookCollection有点误导,因为每个UserBookCollection实际上只是一个用户和一本书之间的链接。
此外,您希望避免在Rails中调用任何Collection
,因为ActiveRecord
使用该概念来表示可能加载或未加载的链接记录的集合 - you're going to confuse others。
这是解决同一问题的一种方法
class User
has_many :reviews
has_many :books, through: :reviews
end
class Review
belongs_to :user
belongs_to :book
end
class Book
has_many :reviews, through: :reviews
end
很好,现在我们有一个对象,我们甚至不必解释它的作用。所以我们把它变成一个RESTful资源:
Rails.application.routes.draw do
resources :reviews, only: [:show, :edit, :update, :destroy]
resources :books do
resources :reviews, only: [:new, :create, :index], module: :books
end
resources :users do
resources :reviews, only: [:index], module: :users
end
end
这为我们提供了具有上下文的路线:
Prefix Verb URI Pattern Controller#Action
edit_review GET /reviews/:id/edit(.:format) reviews#edit
review GET /reviews/:id(.:format) reviews#show
PATCH /reviews/:id(.:format) reviews#update
PUT /reviews/:id(.:format) reviews#update
DELETE /reviews/:id(.:format) reviews#destroy
book_reviews GET /books/:book_id/reviews(.:format) books/reviews#index
POST /books/:book_id/reviews(.:format) books/reviews#create
new_book_review GET /books/:book_id/reviews/new(.:format) books/reviews#new
user_reviews GET /users/:user_id/reviews(.:format) users/reviews#index
酷 - 现在我们有一个内置上下文的审查API。 RESTful路线本身告诉我们何时为某本书创建评论或查看特定用户的评论。
module: :books
告诉rails将嵌套路由链接到“命名空间”控制器,而不是将所有内容都推送到ReviewsController
。
我们按如下方式设置控制器:
class ReviewsController < ApplicationController
before_action :set_review, only: [:edit, :show, :update, :destroy]
# GET /reviews/:id
def show
end
# GET /reviews
def index
@reviews = Review.all
end
# GET /reviews/:id/edit
def edit
end
# PATCH /reviews/:id
def update
@review.update(review_params)
respond_with(@review)
end
# DELETE /reviews/:id
def destroy
@review.destroy
respond_with(@review)
end
private
def set_review
@review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :body) # note that you don't need book_id in here.
end
end
# app/controllers/books/reviews_controller.rb
class Books::ReviewsController < ReviewsController
before_action :set_book
# GET /books/:book_id/reviews
def index
@reviews = Review.eager_load(:user, :book).where(book_id: params[:book_id])
end
# GET /books/:book_id/reviews/new
def new
@review = @book.find(params[:book_id]).reviews.new
end
# POST /books/:book_id/reviews
def create
@review = @book.reviews.new(review_params) do |review|
review.user = current_user
end
respond_with(@review)
end
private
def set_book
@book = Book).find(params[:book_id])
end
end
# app/controllers/users/reviews_controller.rb
class Users::ReviewsController < ApplicationController
# GET /users/:user_id/reviews
def index
@reviews = Review.joins(:user, :book).where(user_id: params[:user_id])
end
end
您可能会质疑为什么要使用3个控制器?它允许代码共享和自定义的非常干净的机制。您可以拥有不同的逻辑和视图,而无需创建大量if
或switch
代码路径。在一个动作中有许多分支使得测试非常混乱并且违反了瘦的控制器范例。
表单看起来像这样:
# app/views/reviews/_fields.html.erb
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :body %> (optional):
<%= f.text_area :body, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
# app/views/books/reviews/new.erb
<%= form_for([@book, @review]) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>
# app/views/books/edit.erb
<%= form_for(@review) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>